<commit_before>"""
JSON API for the Stock app
"""

from django_filters.rest_framework import FilterSet, DjangoFilterBackend
from django_filters import NumberFilter

from django.conf.urls import url, include
from django.urls import reverse

from django.db.models import Sum, Count

from .models import StockLocation, StockItem
from .models import StockItemTracking

from part.models import Part, PartCategory

from .serializers import StockItemSerializer, StockQuantitySerializer
from .serializers import LocationSerializer
from .serializers import StockTrackingSerializer

from InvenTree.views import TreeSerializer
from InvenTree.helpers import str2bool

from rest_framework.serializers import ValidationError
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import generics, response, filters, permissions

class StockCategoryTree(TreeSerializer):
    title = 'Stock'
    model = StockLocation

    @property
    def root_url(self):
        return reverse('stock-index')

class StockDetail(generics.RetrieveUpdateDestroyAPIView):
    """ API detail endpoint for Stock object

    get:
    Return a single StockItem object

    post:
    Update a StockItem

    delete:
    Remove a StockItem
    """

    queryset = StockItem.objects.all()
    serializer_class = StockItemSerializer
    permission_classes = (permissions.IsAuthenticatedOrReadOnly,)

class StockFilter(FilterSet):
    """ FilterSet for advanced stock filtering.

    Allows greater-than / less-than filtering for stock quantity
    """

    min_stock = NumberFilter(name='quantity', lookup_expr='gte')
    max_stock = NumberFilter(name='quantity', lookup_expr='lte')

    class Meta:
        model = StockItem
        fields = ['quantity', 'part', 'location']

class StockStocktake(APIView):
    """ Stocktake API endpoint provides stock update of multiple items simultaneously.
    The 'action' field tells the type of stock action to perform:
    - stocktake: Count the stock item(s)
    - remove: Remove the quantity provided from stock
    - add: Add the quantity provided from stock
    """

    permission_classes = [
        permissions.IsAuthenticatedOrReadOnly,
    ]

    def post(self, request, *args, **kwargs):

        if 'action' not in request.data:
            raise ValidationError({'action': 'Stocktake action must be provided'})

        action = request.data['action']

        ACTIONS = ['stocktake', 'remove', 'add']

        if action not in ACTIONS:
            raise ValidationError({'action': 'Action must be one of ' + ','.join(ACTIONS)})

        elif 'items[]' not in request.data:
            raise ValidationError({'items[]:' 'Request must contain list of items'})

        items = []

        # Ensure each entry is valid
        for entry in request.data['items[]']:
            if 'pk' not in entry:
                raise ValidationError({'pk': 'Each entry must contain pk field'})
            elif 'quantity' not in entry:
                raise ValidationError({'quantity': 'Each entry must contain quantity field'})

            item = {}
            try:
                item['item'] = StockItem.objects.get(pk=entry['pk'])
            except StockItem.DoesNotExist:
                raise ValidationError({'pk': 'No matching StockItem found for pk={pk}'.format(pk=entry['pk'])})
            try:
                item['quantity'] = int(entry['quantity'])
            except ValueError:
                raise ValidationError({'quantity': 'Quantity must be an integer'})

            if item['quantity'] < 0:
                raise ValidationError({'quantity': 'Quantity must be >= 0'})

            items.append(item)

        # Stocktake notes
        notes = ''

        if 'notes' in request.data:
            notes = request.data['notes']

        n = 0

        for item in items:
            quantity = int(item['quantity'])

            if action == u'stocktake':
                if item['item'].stocktake(quantity, request.user, notes=notes):
                    n += 1
            elif action == u'remove':
                if item['item'].take_stock(quantity, request.user, notes=notes):
                    n += 1
            elif action == u'add':
                if item['item'].add_stock(quantity, request.user, notes=notes):
                    n += 1

        return Response({'success': 'Updated stock for {n} items'.format(n=n)})

class StockMove(APIView):
    """ API endpoint for performing stock movements """

    permission_classes = [
        permissions.IsAuthenticatedOrReadOnly,
    ]

    def post(self, request, *args, **kwargs):

        data = request.data

        if 'location' not in data:
            raise ValidationError({'location': 'Destination must be specified'})

        try:
            loc_id = int(data.get('location'))
        except ValueError:
            raise ValidationError({'location': 'Integer ID required'})

        try:
            location = StockLocation.objects.get(pk=loc_id)
        except StockLocation.DoesNotExist:
            raise ValidationError({'location': 'Location does not exist'})

        if 'stock' not in data:
            raise ValidationError({'stock': 'Stock list must be specified'})

        stock_list = data.get('stock')

        if type(stock_list) is not list:
            raise ValidationError({'stock': 'Stock must be supplied as a list'})

        if 'notes' not in data:
            raise ValidationError({'notes': 'Notes field must be supplied'})

        for item in stock_list:
            try:
                stock_id = int(item['pk'])
                if 'quantity' in item:
                    quantity = int(item['quantity'])
                else:
                    # If quantity not supplied, we'll move the entire stock
                    quantity = None
            except ValueError:
                # Ignore this one
                continue

            # Ignore a zero quantity movement
            if quantity <= 0:
                continue

            try:
                stock = StockItem.objects.get(pk=stock_id)
            except StockItem.DoesNotExist:
                continue

            if quantity is None:
                quantity = stock.quantity

            stock.move(location, data.get('notes'), request.user, quantity=quantity)

        return Response({'success': 'Moved parts to {loc}'.format(
            loc=str(location)
        )})

class StockLocationList(generics.ListCreateAPIView):
    """ API endpoint for list view of StockLocation objects:

    - GET: Return list of StockLocation objects
    - POST: Create a new StockLocation
    """

    queryset = StockLocation.objects.all()

    serializer_class = LocationSerializer

    permission_classes = [
        permissions.IsAuthenticatedOrReadOnly,
    ]

    filter_backends = [
        DjangoFilterBackend,
        filters.SearchFilter,
        filters.OrderingFilter,
    ]

    filter_fields = [
        'parent',
    ]

class StockList(generics.ListCreateAPIView):
    """ API endpoint for list view of Stock objects

    - GET: Return a list of all StockItem objects (with optional query filters)
    - POST: Create a new StockItem

    Additional query parameters are available:
        - aggregate: If 'true' then stock items are aggregated by Part and Location
        - location: Filter stock by location
        - category: Filter by parts belonging to a certain category
        - supplier: Filter by supplier
    """

    def get_serializer(self, *args, **kwargs):

        part_detail = str2bool(self.request.GET.get('part_detail', None))
        location_detail = str2bool(self.request.GET.get('location_detail', None))

        kwargs['part_detail'] = part_detail
        kwargs['location_detail'] = location_detail

        kwargs['context'] = self.get_serializer_context()
        return self.serializer_class(*args, **kwargs)

    def list(self, request, *args, **kwargs):

        queryset = self.filter_queryset(self.get_queryset())

        if str2bool(self.request.GET.get('aggregate', None)):
            # Aggregate stock by part type
            queryset = queryset.values(
                'part',
                'part__name',
                'part__image',
                'location',
                'location__name').annotate(
                    stock=Sum('quantity'),
                    items=Count('part'))

            for result in queryset:
                # If there is only 1 stock item (which will be a lot of the time),
                # Add that data to the dict
                if result['items'] == 1:
                    items = StockItem.objects.filter(
                        part=result['part'],
                        location=result['location']
                    )

                    if items.count() == 1:
                        result.pop('items')

                        item = items[0]

                        # Add in some extra information specific to this StockItem
                        result['pk'] = item.id
                        result['serial'] = item.serial
                        result['batch'] = item.batch
                        result['notes'] = item.notes

            return Response(queryset)

        page = self.paginate_queryset(queryset)
        if page is not None:
            serializer = self.get_serializer(page, many=True)
            return self.get_paginated_response(serializer.data)

        serializer = self.get_serializer(queryset, many=True)
        return Response(serializer.data)

    def get_queryset(self):
        """
        If the query includes a particular location,
        we may wish to also request stock items from all child locations.
        """

        # Start with all objects
        stock_list = StockItem.objects.filter(customer=None, belongs_to=None)

        # Does the client wish to filter by the Part ID?
        part_id = self.request.query_params.get('part', None)

        if part_id:
            try:
                part = Part.objects.get(pk=part_id)

                # If the part is a Template part, select stock items for any "variant" parts under that template
                if part.is_template:
                    stock_list = stock_list.filter(part__in=[part.id for part in Part.objects.filter(variant_of=part_id)])
                else:
                    stock_list = stock_list.filter(part=part_id)

            except Part.DoesNotExist:
                pass

        # Does the client wish to filter by stock location?
        loc_id = self.request.query_params.get('location', None)

        if loc_id:
            try:
                location = StockLocation.objects.get(pk=loc_id)
                stock_list = stock_list.filter(location__in=location.getUniqueChildren())

            except StockLocation.DoesNotExist:
                pass

        # Does the client wish to filter by part category?
        cat_id = self.request.query_params.get('category', None)

        if cat_id:
            try:
                category = PartCategory.objects.get(pk=cat_id)
                stock_list = stock_list.filter(part__category__in=category.getUniqueChildren())

            except PartCategory.DoesNotExist:
                pass

        # Filter by supplier
        supplier_id = self.request.query_params.get('supplier', None)

        if supplier_id:
            stock_list = stock_list.filter(supplier_part__supplier=supplier_id)

        # Pre-fetch related objects for better response time
        stock_list = self.get_serializer_class().setup_eager_loading(stock_list)

        # Also ensure that we pre-fecth all the related items
        stock_list = stock_list.prefetch_related(
            'part',
            'part__category',
            'location'
        )

        return stock_list

    serializer_class = StockItemSerializer

    permission_classes = [
        permissions.IsAuthenticatedOrReadOnly,
    ]

    filter_backends = [
        DjangoFilterBackend,
        filters.SearchFilter,
        filters.OrderingFilter,
    ]

    filter_fields = [
        'supplier_part',
        'customer',
        'belongs_to',
        # 'status' TODO - There are some issues filtering based on an enumeration field
    ]

class StockStocktakeEndpoint(generics.UpdateAPIView):
    """ API endpoint for performing stocktake """

    queryset = StockItem.objects.all()
    serializer_class = StockQuantitySerializer
    permission_classes = (permissions.IsAuthenticatedOrReadOnly,)

    def update(self, request, *args, **kwargs):
        object = self.get_object()
        object.stocktake(request.data['quantity'], request.user)

        serializer = self.get_serializer(object)

        return response.Response(serializer.data)

class StockTrackingList(generics.ListCreateAPIView):
    """ API endpoint for list view of StockItemTracking objects.

    StockItemTracking objects are read-only
    (they are created by internal model functionality)

    - GET: Return list of StockItemTracking objects
    """

    queryset = StockItemTracking.objects.all()
    serializer_class = StockTrackingSerializer
    permission_classes = [permissions.IsAuthenticatedOrReadOnly]

    filter_backends = [
        DjangoFilterBackend,
        filters.SearchFilter,
        filters.OrderingFilter,
    ]

    filter_fields = [
        'item',
        'user',
    ]

    ordering = '-date'

    ordering_fields = [
        'date',
    ]

    search_fields = [
        'title',
        'notes',
    ]

class LocationDetail(generics.RetrieveUpdateDestroyAPIView):
    """ API endpoint for detail view of StockLocation object

    - GET: Return a single StockLocation object
    - PATCH: Update a StockLocation object
    - DELETE: Remove a StockLocation object
    """

    queryset = StockLocation.objects.all()
    serializer_class = LocationSerializer
    permission_classes = (permissions.IsAuthenticatedOrReadOnly,)

stock_endpoints = [
    url(r'^$', StockDetail.as_view(), name='api-stock-detail'),
]

location_endpoints = [
    url(r'^$', LocationDetail.as_view(), name='api-location-detail'),
]

stock_api_urls = [
    url(r'location/?', StockLocationList.as_view(), name='api-location-list'),

    url(r'location/(?P<pk>\d+)/', include(location_endpoints)),

    url(r'stocktake/?', StockStocktake.as_view(), name='api-stock-stocktake'),

    url(r'move/?', StockMove.as_view(), name='api-stock-move'),

    url(r'track/?', StockTrackingList.as_view(), name='api-stock-track'),

    url(r'^tree/?', StockCategoryTree.as_view(), name='api-stock-tree'),

    # Detail for a single stock item
    url(r'^(?P<pk>\d+)/', include(stock_endpoints)),

    url(r'^.*$', StockList.as_view(), name='api-stock-list'),
]
<commit_msg>And deltaco some code that went along with it<commit_after>"""
JSON API for the Stock app
"""

from django_filters.rest_framework import FilterSet, DjangoFilterBackend
from django_filters import NumberFilter

from django.conf.urls import url, include
from django.urls import reverse

from django.db.models import Sum, Count

from .models import StockLocation, StockItem
from .models import StockItemTracking

from part.models import Part, PartCategory

from .serializers import StockItemSerializer, StockQuantitySerializer
from .serializers import LocationSerializer
from .serializers import StockTrackingSerializer

from InvenTree.views import TreeSerializer
from InvenTree.helpers import str2bool

from rest_framework.serializers import ValidationError
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import generics, response, filters, permissions

class StockCategoryTree(TreeSerializer):
    title = 'Stock'
    model = StockLocation

    @property
    def root_url(self):
        return reverse('stock-index')

class StockDetail(generics.RetrieveUpdateDestroyAPIView):
    """ API detail endpoint for Stock object

    get:
    Return a single StockItem object

    post:
    Update a StockItem

    delete:
    Remove a StockItem
    """

    queryset = StockItem.objects.all()
    serializer_class = StockItemSerializer
    permission_classes = (permissions.IsAuthenticatedOrReadOnly,)

class StockFilter(FilterSet):
    """ FilterSet for advanced stock filtering.

    Allows greater-than / less-than filtering for stock quantity
    """

    min_stock = NumberFilter(name='quantity', lookup_expr='gte')
    max_stock = NumberFilter(name='quantity', lookup_expr='lte')

    class Meta:
        model = StockItem
        fields = ['quantity', 'part', 'location']

class StockStocktake(APIView):
    """ Stocktake API endpoint provides stock update of multiple items simultaneously.
    The 'action' field tells the type of stock action to perform:
    - stocktake: Count the stock item(s)
    - remove: Remove the quantity provided from stock
    - add: Add the quantity provided from stock
    """

    permission_classes = [
        permissions.IsAuthenticatedOrReadOnly,
    ]

    def post(self, request, *args, **kwargs):

        if 'action' not in request.data:
            raise ValidationError({'action': 'Stocktake action must be provided'})

        action = request.data['action']

        ACTIONS = ['stocktake', 'remove', 'add']

        if action not in ACTIONS:
            raise ValidationError({'action': 'Action must be one of ' + ','.join(ACTIONS)})

        elif 'items[]' not in request.data:
            raise ValidationError({'items[]:' 'Request must contain list of items'})

        items = []

        # Ensure each entry is valid
        for entry in request.data['items[]']:
            if 'pk' not in entry:
                raise ValidationError({'pk': 'Each entry must contain pk field'})
            elif 'quantity' not in entry:
                raise ValidationError({'quantity': 'Each entry must contain quantity field'})

            item = {}
            try:
                item['item'] = StockItem.objects.get(pk=entry['pk'])
            except StockItem.DoesNotExist:
                raise ValidationError({'pk': 'No matching StockItem found for pk={pk}'.format(pk=entry['pk'])})
            try:
                item['quantity'] = int(entry['quantity'])
            except ValueError:
                raise ValidationError({'quantity': 'Quantity must be an integer'})

            if item['quantity'] < 0:
                raise ValidationError({'quantity': 'Quantity must be >= 0'})

            items.append(item)

        # Stocktake notes
        notes = ''

        if 'notes' in request.data:
            notes = request.data['notes']

        n = 0

        for item in items:
            quantity = int(item['quantity'])

            if action == u'stocktake':
                if item['item'].stocktake(quantity, request.user, notes=notes):
                    n += 1
            elif action == u'remove':
                if item['item'].take_stock(quantity, request.user, notes=notes):
                    n += 1
            elif action == u'add':
                if item['item'].add_stock(quantity, request.user, notes=notes):
                    n += 1

        return Response({'success': 'Updated stock for {n} items'.format(n=n)})

class StockMove(APIView):
    """ API endpoint for performing stock movements """

    permission_classes = [
        permissions.IsAuthenticatedOrReadOnly,
    ]

    def post(self, request, *args, **kwargs):

        data = request.data

        if 'location' not in data:
            raise ValidationError({'location': 'Destination must be specified'})

        try:
            loc_id = int(data.get('location'))
        except ValueError:
            raise ValidationError({'location': 'Integer ID required'})

        try:
            location = StockLocation.objects.get(pk=loc_id)
        except StockLocation.DoesNotExist:
            raise ValidationError({'location': 'Location does not exist'})

        if 'stock' not in data:
            raise ValidationError({'stock': 'Stock list must be specified'})

        stock_list = data.get('stock')

        if type(stock_list) is not list:
            raise ValidationError({'stock': 'Stock must be supplied as a list'})

        if 'notes' not in data:
            raise ValidationError({'notes': 'Notes field must be supplied'})

        for item in stock_list:
            try:
                stock_id = int(item['pk'])
                if 'quantity' in item:
                    quantity = int(item['quantity'])
                else:
                    # If quantity not supplied, we'll move the entire stock
                    quantity = None
            except ValueError:
                # Ignore this one
                continue

            # Ignore a zero quantity movement
            if quantity <= 0:
                continue

            try:
                stock = StockItem.objects.get(pk=stock_id)
            except StockItem.DoesNotExist:
                continue

            if quantity is None:
                quantity = stock.quantity

            stock.move(location, data.get('notes'), request.user, quantity=quantity)

        return Response({'success': 'Moved parts to {loc}'.format(
            loc=str(location)
        )})

class StockLocationList(generics.ListCreateAPIView):
    """ API endpoint for list view of StockLocation objects:

    - GET: Return list of StockLocation objects
    - POST: Create a new StockLocation
    """

    queryset = StockLocation.objects.all()

    serializer_class = LocationSerializer

    permission_classes = [
        permissions.IsAuthenticatedOrReadOnly,
    ]

    filter_backends = [
        DjangoFilterBackend,
        filters.SearchFilter,
        filters.OrderingFilter,
    ]

    filter_fields = [
        'parent',
    ]

class StockList(generics.ListCreateAPIView):
    """ API endpoint for list view of Stock objects

    - GET: Return a list of all StockItem objects (with optional query filters)
    - POST: Create a new StockItem

    Additional query parameters are available:
        - aggregate: If 'true' then stock items are aggregated by Part and Location
        - location: Filter stock by location
        - category: Filter by parts belonging to a certain category
        - supplier: Filter by supplier
    """

    def get_serializer(self, *args, **kwargs):

        part_detail = str2bool(self.request.GET.get('part_detail', None))
        location_detail = str2bool(self.request.GET.get('location_detail', None))

        kwargs['part_detail'] = part_detail
        kwargs['location_detail'] = location_detail

        kwargs['context'] = self.get_serializer_context()
        return self.serializer_class(*args, **kwargs)

    def list(self, request, *args, **kwargs):

        queryset = self.filter_queryset(self.get_queryset())

        if str2bool(self.request.GET.get('aggregate', None)):
            # Aggregate stock by part type
            queryset = queryset.values(
                'part',
                'part__name',
                'part__image',
                'location',
                'location__name').annotate(
                    stock=Sum('quantity'),
                    items=Count('part'))

            for result in queryset:
                # If there is only 1 stock item (which will be a lot of the time),
                # Add that data to the dict
                if result['items'] == 1:
                    items = StockItem.objects.filter(
                        part=result['part'],
                        location=result['location']
                    )

                    if items.count() == 1:
                        result.pop('items')

                        item = items[0]

                        # Add in some extra information specific to this StockItem
                        result['pk'] = item.id
                        result['serial'] = item.serial
                        result['batch'] = item.batch
                        result['notes'] = item.notes

            return Response(queryset)

        page = self.paginate_queryset(queryset)
        if page is not None:
            serializer = self.get_serializer(page, many=True)
            return self.get_paginated_response(serializer.data)

        serializer = self.get_serializer(queryset, many=True)
        return Response(serializer.data)

    def get_queryset(self):
        """
        If the query includes a particular location,
        we may wish to also request stock items from all child locations.
        """

        # Start with all objects
        stock_list = StockItem.objects.filter(customer=None, belongs_to=None)

        # Does the client wish to filter by the Part ID?
        part_id = self.request.query_params.get('part', None)

        if part_id:
            try:
                part = Part.objects.get(pk=part_id)

                # If the part is a Template part, select stock items for any "variant" parts under that template
                if part.is_template:
                    stock_list = stock_list.filter(part__in=[part.id for part in Part.objects.filter(variant_of=part_id)])
                else:
                    stock_list = stock_list.filter(part=part_id)

            except Part.DoesNotExist:
                pass

        # Does the client wish to filter by stock location?
        loc_id = self.request.query_params.get('location', None)

        if loc_id:
            try:
                location = StockLocation.objects.get(pk=loc_id)
                stock_list = stock_list.filter(location__in=location.getUniqueChildren())

            except StockLocation.DoesNotExist:
                pass

        # Does the client wish to filter by part category?
        cat_id = self.request.query_params.get('category', None)

        if cat_id:
            try:
                category = PartCategory.objects.get(pk=cat_id)
                stock_list = stock_list.filter(part__category__in=category.getUniqueChildren())

            except PartCategory.DoesNotExist:
                pass

        # Filter by supplier
        supplier_id = self.request.query_params.get('supplier', None)

        if supplier_id:
            stock_list = stock_list.filter(supplier_part__supplier=supplier_id)

        # Also ensure that we pre-fecth all the related items
        stock_list = stock_list.prefetch_related(
            'part',
            'part__category',
            'location'
        )

        return stock_list

    serializer_class = StockItemSerializer

    permission_classes = [
        permissions.IsAuthenticatedOrReadOnly,
    ]

    filter_backends = [
        DjangoFilterBackend,
        filters.SearchFilter,
        filters.OrderingFilter,
    ]

    filter_fields = [
        'supplier_part',
        'customer',
        'belongs_to',
        # 'status' TODO - There are some issues filtering based on an enumeration field
    ]

class StockStocktakeEndpoint(generics.UpdateAPIView):
    """ API endpoint for performing stocktake """

    queryset = StockItem.objects.all()
    serializer_class = StockQuantitySerializer
    permission_classes = (permissions.IsAuthenticatedOrReadOnly,)

    def update(self, request, *args, **kwargs):
        object = self.get_object()
        object.stocktake(request.data['quantity'], request.user)

        serializer = self.get_serializer(object)

        return response.Response(serializer.data)

class StockTrackingList(generics.ListCreateAPIView):
    """ API endpoint for list view of StockItemTracking objects.

    StockItemTracking objects are read-only
    (they are created by internal model functionality)

    - GET: Return list of StockItemTracking objects
    """

    queryset = StockItemTracking.objects.all()
    serializer_class = StockTrackingSerializer
    permission_classes = [permissions.IsAuthenticatedOrReadOnly]

    filter_backends = [
        DjangoFilterBackend,
        filters.SearchFilter,
        filters.OrderingFilter,
    ]

    filter_fields = [
        'item',
        'user',
    ]

    ordering = '-date'

    ordering_fields = [
        'date',
    ]

    search_fields = [
        'title',
        'notes',
    ]

class LocationDetail(generics.RetrieveUpdateDestroyAPIView):
    """ API endpoint for detail view of StockLocation object

    - GET: Return a single StockLocation object
    - PATCH: Update a StockLocation object
    - DELETE: Remove a StockLocation object
    """

    queryset = StockLocation.objects.all()
    serializer_class = LocationSerializer
    permission_classes = (permissions.IsAuthenticatedOrReadOnly,)

stock_endpoints = [
    url(r'^$', StockDetail.as_view(), name='api-stock-detail'),
]

location_endpoints = [
    url(r'^$', LocationDetail.as_view(), name='api-location-detail'),
]

stock_api_urls = [
    url(r'location/?', StockLocationList.as_view(), name='api-location-list'),

    url(r'location/(?P<pk>\d+)/', include(location_endpoints)),

    url(r'stocktake/?', StockStocktake.as_view(), name='api-stock-stocktake'),

    url(r'move/?', StockMove.as_view(), name='api-stock-move'),

    url(r'track/?', StockTrackingList.as_view(), name='api-stock-track'),

    url(r'^tree/?', StockCategoryTree.as_view(), name='api-stock-tree'),

    # Detail for a single stock item
    url(r'^(?P<pk>\d+)/', include(stock_endpoints)),

    url(r'^.*$', StockList.as_view(), name='api-stock-list'),
]
<|endoftext|>


<commit_before>"""
distribution.py
Author: Payton
Credit: Avery, Daniel, Mr. Dennison

Assignment:

Write and submit a Python program (distribution.py) that computes and displays
the distribution of characters in a given sample of text.

Output of your program should look like this:

Please enter a string of text (the bigger the better): The rain in Spain stays mainly in the plain.
The distribution of characters in "The rain in Spain stays mainly in the plain." is:
iiiiii
nnnnnn
aaaaa
sss
ttt
ee
hh
ll
pp
yy
m
r

Notice about this example:

* The text: 'The rain ... plain' is provided by the user as input to your program.
* Uppercase characters are converted to lowercase
* Spaces and punctuation marks are ignored completely.
* Characters that are more common appear first in the list.
* Where the same number of characters occur, the lines are ordered alphabetically.
  For example, in the printout above, the letters e, h, l, p and y both occur twice
  in the text and they are listed in the output in alphabetical order.
* Letters that do not occur in the text are not listed in the output at all.
"""
def compare(a, b):
    return b > a

o = str(input("Please enter a string of text (the bigger the better): "))
print('The distribution of character in "' + o + '" is: ')

orig = o.lower()
alph = "abcdefghijklmnopqrstuvwxyz"
results = []
listnum = []

for c in alph:
    r = orig.count(c)
    if not r == 0:
        t = (r*c)
        results.append(t)
        listnum.append(r)

lists=zip(listnum,results)
print(list(lists))

list1 = sorted(results, key=lambda results: listnum[2])
list1.sort(reverse=True)
print(list(list1))
l=len(x[1] for x in list1)
for y in range(0,l-1):
    if not y==y+1:
        a=list([r[1] for r in list1])
        print(a[y])

def bsort(seq, cmp):
    sorted = False
    while not sorted:
        sorted = True
        for index, value in enumerate(seq):
            if index > 0:
                if not cmp(seq[index-1], value):
                    sorted = False
                    seq[index-1], seq[index] = seq[index], seq[index-1]

bsort(results, compare)
print(results)
<commit_msg>Updated from Brython Server: 2/26/2016 2:31:04 PM<commit_after>"""
distribution.py
Author: Payton
Credit: Avery, Daniel, Mr. Dennison

Assignment:

Write and submit a Python program (distribution.py) that computes and displays
the distribution of characters in a given sample of text.

Output of your program should look like this:

Please enter a string of text (the bigger the better): The rain in Spain stays mainly in the plain.
The distribution of characters in "The rain in Spain stays mainly in the plain." is:
iiiiii
nnnnnn
aaaaa
sss
ttt
ee
hh
ll
pp
yy
m
r

Notice about this example:

* The text: 'The rain ... plain' is provided by the user as input to your program.
* Uppercase characters are converted to lowercase
* Spaces and punctuation marks are ignored completely.
* Characters that are more common appear first in the list.
* Where the same number of characters occur, the lines are ordered alphabetically.
  For example, in the printout above, the letters e, h, l, p and y both occur twice
  in the text and they are listed in the output in alphabetical order.
* Letters that do not occur in the text are not listed in the output at all.
"""
def compare(a, b):
    return b > a

o = str(input("Please enter a string of text (the bigger the better): "))
print('The distribution of character in "' + o + '" is: ')

orig = o.lower()
alph = "abcdefghijklmnopqrstuvwxyz"
results = []
listnum = []

for c in alph:
    r = orig.count(c)
    if not r == 0:
        t = (r*c)
        results.append(t)
        listnum.append(r)

lists=zip(listnum,results)
print(list(lists))

list1 = sorted(results, key=lambda results: listnum[2])
list1.sort(reverse=True)
print(list(list1))
l=len(x[1] for x in lists)
for y in range(0,l-1):
    if not y==y+1:
        a=list([r[1] for r in lists])
        print(a[y])

def bsort(seq, cmp):
    sorted = False
    while not sorted:
        sorted = True
        for index, value in enumerate(seq):
            if index > 0:
                if not cmp(seq[index-1], value):
                    sorted = False
                    seq[index-1], seq[index] = seq[index], seq[index-1]

bsort(results, compare)
print(results)
<|endoftext|>


<commit_before>import os
import numpy as np
import pandas as pd
from math import exp, log

pd.options.mode.chained_assignment = None # turns off Pandas warning about dataframe overwrites

isotopes = pd.read_csv('chem.csv', index_col=0) # loads isotope data from a local source
# Note that all chemical abundance data is given in ppm due to trace nature of Hf-Ta-W system
def f(x):
    try:
        return np.float(x)
    except:
        return np.nan
isotopes["Abundance"] = isotopes['Abundance'].apply(f) # convert Abundances column to floats or NaN
time = 0 # time, in years
time_resolution = 1000000.0 # time resolution, in years
time_limit = (4.5 * 10**9) # limits the time evolution to the age of the Earth
iteration = 0 # the number of iterations the model has been exposed to
max_iterations = round(time_limit / time_resolution) # the maximum number of iterations (default: age of Earth 4.5Gya)

class partition():

    def __init__(self, element):
        self.element = element

class decay:

    """
    Models decay of of a radioactive isotope family tree.
    """

    def __init__(self, element):
        self.element = element

    def rad_decay(self, isotope_df, time_resolution):
        daughters = [] # first element will be the primordial parent, all else daughters in descending order
        daughters.append(self.element) # adds primordial parent
        if isotope_df['Daughter'][self.element] != 'NONE': # checks to see that isotope has a daughter
            daughter = str(isotope_df['Daughter'][self.element])
            while daughter != 'NONE': # adds daughters to list in stepwise fashion
                daughters.append(daughter)
                daughter = str(isotope_df['Daughter'][daughter])
        if len(daughters) > 1: # check to see that primordial isotope has daughters, i.e. is radioactive
            iterdaughters = iter(reversed(daughters)) # now stable isotope is first in list, primordial parent last
            next(iterdaughters) # skips the stable isotope
            for i in iterdaughters: # iterates through daughters to model decay
                half_life = float(isotope_df['Half-Life'][i]) # extracts half-life
                daughter = isotope_df['Daughter'][i] # identifies daughter
                curr_amount = float(isotope_df['Abundance'][i]) # finds current abundance of isotope in question
                k = log(0.5) / half_life # gamma variable in decay equation
                remaining_amount = curr_amount * exp(k * time_resolution) # models remaining amount of decay, where time is the time resolution
                isotope_df['Abundance'][i] = remaining_amount
                if isotope_df['Daughter'][i] != 'NONE':
                    isotope_df['Abundance'][daughter] = float(isotope_df['Abundance'][daughter] + (curr_amount - remaining_amount))
                else:
                    pass
        else: # no daughters, doesn't decay into daughter products
            pass
        return isotope_df # returns the updated isotope dataframe for one iteration

hf_182 = decay(element='182-Hf')
while time < 55000000:
    hf_182.rad_decay(isotope_df=isotopes, time_resolution=time_resolution)
    time += time_resolution
    iteration += 1
    # print("\nIterations: {}\nTime: {} years\n182-Hf: {} mol\n182-Ta: {} mol\n182-W: {} mol".format(iteration, time,
    #                                                                                isotopes['Abundance'][
    #                                                                                '182-Hf'],
    #                                                                                isotopes['Abundance'][
    #                                                                                '182-Ta'],
    #                                                                                isotopes['Abundance'][
    #                                                                                '182-W']))
print("\nIsotope Dataframe:")
print(isotopes)
print("Time: {}, Iterations: {}".format(time, iteration))

# hf_182 = decay(element='182-Hf') # opens an instance of the decay class for the primordial isotope, keep formatting!
# for i in range(3):
#     if isotopes['Abundance']['182-Hf'] != 0:
#         run_decay = hf_182.rad_decay(isotope_df=isotopes, time_resolution=time_resolution)
#         isotopes['Abundance'] = run_decay['Abundance']
#         time += time_resolution
#         iteration += 1
#         print("\nIterations: {}\nTime: {} years\n182-Hf: {} mol\n182-Ta: {} mol\n182-W: {} mol".format(iteration, time,
#                                                                                isotopes['Abundance'][
#                                                                                '182-Hf'],
#                                                                                isotopes['Abundance'][
#                                                                                '182-Ta'],
#                                                                                isotopes['Abundance'][
#                                                                                '182-W']))

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


<commit_before>"""
distribution.py
Author: Roger Danilek
Credit: Adam Pikielny, Stack Overflow

Assignment:

Write and submit a Python program (distribution.py) that computes and displays
the distribution of characters in a given sample of text.

Output of your program should look like this:

Please enter a string of text (the bigger the better): The rain in Spain stays mainly in the plain.
The distribution of characters in "The rain in Spain stays mainly in the plain." is:
iiiiii
nnnnnn
aaaaa
sss
ttt
ee
hh
ll
pp
yy
m
r

Notice about this example:

* The text: 'The rain ... plain' is provided by the user as input to your program.
* Uppercase characters are converted to lowercase
* Spaces and punctuation marks are ignored completely.
* Characters that are more common appear first in the list.
* Where the same number of characters occur, the lines are ordered alphabetically.
  For example, in the printout above, the letters e, h, l, p and y both occur twice
  in the text and they are listed in the output in alphabetical order.
* Letters that do not occur in the text are not listed in the output at all.
"""
import string
alpha = string.ascii_lowercase

a = input("Please enter a string of text (the bigger the better): ")

counts = []
for i in list(alpha):
    counts.append(a.count(i)).sort()
zipper = list(zip(counts,a))
print(zipper)

#print("The distribution of characters in "+ a +" is: ")

<commit_msg>Updated from Brython Server: September 29, 2015 2:35:08 PM GMT-4<commit_after>"""
distribution.py
Author: Roger Danilek
Credit: Adam Pikielny, Stack Overflow

Assignment:

Write and submit a Python program (distribution.py) that computes and displays
the distribution of characters in a given sample of text.

Output of your program should look like this:

Please enter a string of text (the bigger the better): The rain in Spain stays mainly in the plain.
The distribution of characters in "The rain in Spain stays mainly in the plain." is:
iiiiii
nnnnnn
aaaaa
sss
ttt
ee
hh
ll
pp
yy
m
r

Notice about this example:

* The text: 'The rain ... plain' is provided by the user as input to your program.
* Uppercase characters are converted to lowercase
* Spaces and punctuation marks are ignored completely.
* Characters that are more common appear first in the list.
* Where the same number of characters occur, the lines are ordered alphabetically.
  For example, in the printout above, the letters e, h, l, p and y both occur twice
  in the text and they are listed in the output in alphabetical order.
* Letters that do not occur in the text are not listed in the output at all.
"""
import string
alpha = string.ascii_lowercase

a = input("Please enter a string of text (the bigger the better): ")

counts = []
for i in list(alpha):
    counts.append(a.count(i)).sort()
zipper = list(zip(counts,alpha))
print(zipper)

#print("The distribution of characters in "+ a +" is: ")

<|endoftext|>


<commit_before>"""
distribution.py
Author: Andy
Credit: <list sources used, if any>

Assignment:

Write and submit a Python program (distribution.py) that computes and displays
the distribution of characters in a given sample of text.

Output of your program should look like this:

Please enter a string of text (the bigger the better): The rain in Spain stays mainly in the plain.
The distribution of characters in "The rain in Spain stays mainly in the plain." is:
iiiiii
nnnnnn
aaaaa
sss
ttt
ee
hh
ll
pp
yy
m
r

Notice about this example:

* The text: 'The rain ... plain' is provided by the user as input to your program.
* Uppercase characters are converted to lowercase
* Spaces and punctuation marks are ignored completely.
* Characters that are more common appear first in the list.
* Where the same number of characters occur, the lines are ordered alphabetically.
  For example, in the printout above, the letters e, h, l, p and y both occur twice
  in the text and they are listed in the output in alphabetical order.
* Letters that do not occur in the text are not listed in the output at all.
"""
alphabet = ["a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z"]
strin = input("Please enter a string of text (the bigger the better): ")
strin = strin.lower()
print (strin)<commit_msg>Updated from Brython Server: 10/12/2016 10:51:26 AM<commit_after>"""
distribution.py
Author: Andy
Credit: Milo the Almighty Savior of ALL Existence

Assignment:

Write and submit a Python program (distribution.py) that computes and displays
the distribution of characters in a given sample of text.

Output of your program should look like this:

Please enter a string of text (the bigger the better): The rain in Spain stays mainly in the plain.
The distribution of characters in "The rain in Spain stays mainly in the plain." is:
iiiiii
nnnnnn
aaaaa
sss
ttt
ee
hh
ll
pp
yy
m
r

Notice about this example:

* The text: 'The rain ... plain' is provided by the user as input to your program.
* Uppercase characters are converted to lowercase
* Spaces and punctuation marks are ignored completely.
* Characters that are more common appear first in the list.
* Where the same number of characters occur, the lines are ordered alphabetically.
  For example, in the printout above, the letters e, h, l, p and y both occur twice
  in the text and they are listed in the output in alphabetical order.
* Letters that do not occur in the text are not listed in the output at all.
"""
alphabet = ["a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z"]
strin = input("Please enter a string of text (the bigger the better): ")
strin = strin.lower()
strlist=list(strin)
for j in range(100):
    for x in strlist:
        if x not in alphabet:
            strlist.remove(x)

print (strlist)
for i in alphabet:
    listname = [" "]
    numval = ""
    for p in strlist:
        if i in strlist:
            numval = numval + i
            strlist.remove(i)
    listname.append(numval)
listname.remove(" ")
print (listname)<|endoftext|>


<commit_before>"""
distribution.py
Author: Avery Wallis
Credit: Ethan, Payton, Doniel

Assignment:

Write and submit a Python program (distribution.py) that computes and displays
the distribution of characters in a given sample of text.

Output of your program should look like this:

Please enter a string of text (the bigger the better): The rain in Spain stays mainly in the plain.
The distribution of characters in "The rain in Spain stays mainly in the plain." is:
iiiiii
nnnnnn
aaaaa
sss
ttt
ee
hh
ll
pp
yy
m
r

Notice about this example:

* The text: 'The rain ... plain' is provided by the user as input to your program.
* Uppercase characters are converted to lowercase
* Spaces and punctuation marks are ignored completely.
* Characters that are more common appear first in the list.
* Where the same number of characters occur, the lines are ordered alphabetically.
  For example, in the printout above, the letters e, h, l, p and y both occur twice
  in the text and they are listed in the output in alphabetical order.
* Letters that do not occur in the text are not listed in the output at all.
"""
def compare(a, b):
    """
    compare - generic comparison function for testing two elements.
    """
    return b > a

string= str(input("Please enter a string of text (the bigger the better): "))
string1=string.lower()
print('The distribution of characters in "'+string1+ '" is:')

alph='abcdefghijklmnopqrstuvwxyz'
result=[]
listnum=[]
thing=[]
for c in alph:
    r = string.count(c)
    if not r==0:
        t=(r*c)
        result.append(t)
        listnum.append(r)
l=len(result)

lists=zip(listnum,result)
result=sorted(lists, key=lambda listnum: listnum[0])   # sort by number
result.sort(reverse=True)
listnum2, result2=zip(*lists)
print(result2)

'''
https://docs.python.org/3/howto/sorting.html#sortinghowto
'''

"""
def bsort(seq, cmp):
    '''
    bsort - simple sorting algorithm that uses any comparison function
    seq - a list to be sorted
    cmp - a function for comparing two elements of seq
    '''
    sorted = False  # assume the seq is not sorted to start with
    while not sorted:
        sorted = True   # assume it's already sorted correctly
        for index, value in enumerate(seq): # for every element in seq
            if index > 0:                   # past the first..
                if not cmp(seq[index-1], value):  # if this element is out of order
                    sorted = False          # then the list is not sorted yet
                    seq[index-1], seq[index] = seq[index], seq[index-1] # and swap it

bsort(result, compare)
print(result)
"""
<commit_msg>Updated from Brython Server: 2/25/2016 4:00:27 PM<commit_after>"""
distribution.py
Author: Avery Wallis
Credit: Ethan, Payton, Doniel

Assignment:

Write and submit a Python program (distribution.py) that computes and displays
the distribution of characters in a given sample of text.

Output of your program should look like this:

Please enter a string of text (the bigger the better): The rain in Spain stays mainly in the plain.
The distribution of characters in "The rain in Spain stays mainly in the plain." is:
iiiiii
nnnnnn
aaaaa
sss
ttt
ee
hh
ll
pp
yy
m
r

Notice about this example:

* The text: 'The rain ... plain' is provided by the user as input to your program.
* Uppercase characters are converted to lowercase
* Spaces and punctuation marks are ignored completely.
* Characters that are more common appear first in the list.
* Where the same number of characters occur, the lines are ordered alphabetically.
  For example, in the printout above, the letters e, h, l, p and y both occur twice
  in the text and they are listed in the output in alphabetical order.
* Letters that do not occur in the text are not listed in the output at all.
"""
def compare(a, b):
    """
    compare - generic comparison function for testing two elements.
    """
    return b > a

string= str(input("Please enter a string of text (the bigger the better): "))
string1=string.lower()
print('The distribution of characters in "'+string1+ '" is:')

alph='abcdefghijklmnopqrstuvwxyz'
result=[]
listnum=[]
thing=[]
for c in alph:
    r = string.count(c)
    if not r==0:
        t=(r*c)
        result.append(t)
        listnum.append(r)
l=len(result)

lists=zip(listnum,result)
result=sorted(lists, key=lambda listnum: listnum[0])   # sort by number
result.sort(reverse=True)

print(result2)

'''
https://docs.python.org/3/howto/sorting.html#sortinghowto
'''

"""
def bsort(seq, cmp):
    '''
    bsort - simple sorting algorithm that uses any comparison function
    seq - a list to be sorted
    cmp - a function for comparing two elements of seq
    '''
    sorted = False  # assume the seq is not sorted to start with
    while not sorted:
        sorted = True   # assume it's already sorted correctly
        for index, value in enumerate(seq): # for every element in seq
            if index > 0:                   # past the first..
                if not cmp(seq[index-1], value):  # if this element is out of order
                    sorted = False          # then the list is not sorted yet
                    seq[index-1], seq[index] = seq[index], seq[index-1] # and swap it

bsort(result, compare)
print(result)
"""
<|endoftext|>


<commit_before>#! /usr/bin/env python

import argparse
import csv
from datetime import datetime

class DKB(csv.Dialect):
    delimiter = ';'
    quotechar = '"'
    doublequote = True
    skipinitialspace = False
    lineterminator = '\r\n'
    quoting = csv.QUOTE_MINIMAL

csv.register_dialect("dkb", DKB)

dkb_field_names = ["buchungstag",
                   "wertstellung",
                   "buchungstext",
                   "beguenstigter",
                   "verwendungszweck",
                   "kontonummer",
                   "blz",
                   "betrag",
                   "glaeubigerID",
                   "mandatsreferenz",
                   "kundenreferenz"]

visa_field_names = ["abgerechnet",
                    "wertstellung",
                    "belegdatum",
                    "umsatzbeschreibung",
                    "betrag",
                    "urspruenglicherBetrag"]

homebank_field_names = ["date",
                        "paymode",
                        "info",
                        "payee",
                        "memo",
                        "amount",
                        "category",
                        "tags"]

def convert_DKB_cash(filename):
    with open(filename, 'r', encoding='iso-8859-1') as csvfile:
        dialect = csv.Sniffer().sniff(csvfile.read(1024))
        csvfile.seek(0)
        reader = csv.DictReader(transaction_lines(csvfile), dialect=dialect, fieldnames=dkb_field_names)

        with open("cashHomebank.csv", 'w') as outfile:
            writer = csv.DictWriter(outfile, dialect='dkb', fieldnames=homebank_field_names)
            for row in reader:
                writer.writerow(
                    {
                        'date': convert_date(row["buchungstag"]),
                        'paymode': 8,
                        'info': None,
                        'payee': row["beguenstigter"],
                        'memo': row["verwendungszweck"],
                        'amount': row["betrag"],
                        'category': None,
                        'tags': None
                    })

def convert_visa(filename):
    with open(filename, 'r', encoding='iso-8859-1') as csvfile:
        dialect = csv.Sniffer().sniff(csvfile.read(1024))
        csvfile.seek(0)
        reader = csv.DictReader(transaction_lines(csvfile), dialect=dialect, fieldnames=visa_field_names)

        with open("visaHomebank.csv", 'w') as outfile:
            writer = csv.DictWriter(outfile, dialect='dkb', fieldnames=homebank_field_names)
            for row in reader:
                writer.writerow(
                    {
                        'date': convert_date(row["wertstellung"]),
                        'paymode': 1,
                        'info': None,
                        'payee': None,
                        'memo': row["umsatzbeschreibung"],
                        'amount': row["betrag"],
                        'category': None,
                        'tags': None
                    })

def transaction_lines(file):
    lines = file.readlines()
    i = 1
    for line in lines:
        if "Betrag" in line:
            return lines[i:]
        i = i + 1

    raise ValueError("Can't convert CSV file without header line")

def convert_date(date_string):
    date = datetime.strptime(date_string, "%d.%m.%Y")
    return date.strftime('%d-%m-%Y')

def main():
    parser = argparse.ArgumentParser(description=
                                     "Convert a CSV export file from DKB online banking "
                                     "to a Homebank compatible CSV format.")
    parser.add_argument("filename", help="The CSV file to convert.")

    group = parser.add_mutually_exclusive_group()
    group.add_argument("-v", "--visa", action="store_true", help="convert a DKB Visa account CSV file")
    group.add_argument("-c", "--cash", action="store_true", help="convert a DKB Cash account CSV file")

    args = parser.parse_args()

    if args.visa:
        convert_visa(args.filename)
        print("DKB Visa file converted. Output file: 'visaHomebank.csv'")
    elif args.cash:
        convert_DKB_cash(args.filename)
        print("DKB Cash file converted. Output file: 'cashHomebank.csv'")
    else:
        print("You must provide the type of the CSV file (--cash for DKB Cash, --visa for DKB Visa)")

if __name__ == '__main__':
    main()
<commit_msg>Add documentation<commit_after>#! /usr/bin/env python

import argparse
import csv
from datetime import datetime

class DKB(csv.Dialect):
    delimiter = ';'
    quotechar = '"'
    doublequote = True
    skipinitialspace = False
    lineterminator = '\r\n'
    quoting = csv.QUOTE_MINIMAL

csv.register_dialect("dkb", DKB)

dkb_field_names = ["buchungstag",
                   "wertstellung",
                   "buchungstext",
                   "beguenstigter",
                   "verwendungszweck",
                   "kontonummer",
                   "blz",
                   "betrag",
                   "glaeubigerID",
                   "mandatsreferenz",
                   "kundenreferenz"]

visa_field_names = ["abgerechnet",
                    "wertstellung",
                    "belegdatum",
                    "umsatzbeschreibung",
                    "betrag",
                    "urspruenglicherBetrag"]

homebank_field_names = ["date",
                        "paymode",
                        "info",
                        "payee",
                        "memo",
                        "amount",
                        "category",
                        "tags"]

def convert_DKB_cash(filename):
    with open(filename, 'r', encoding='iso-8859-1') as csvfile:
        dialect = csv.Sniffer().sniff(csvfile.read(1024))
        csvfile.seek(0)
        reader = csv.DictReader(transaction_lines(csvfile), dialect=dialect, fieldnames=dkb_field_names)

        with open("cashHomebank.csv", 'w') as outfile:
            writer = csv.DictWriter(outfile, dialect='dkb', fieldnames=homebank_field_names)
            for row in reader:
                writer.writerow(
                    {
                        'date': convert_date(row["buchungstag"]),
                        'paymode': 8,
                        'info': None,
                        'payee': row["beguenstigter"],
                        'memo': row["verwendungszweck"],
                        'amount': row["betrag"],
                        'category': None,
                        'tags': None
                    })

def convert_visa(filename):
    with open(filename, 'r', encoding='iso-8859-1') as csvfile:
        dialect = csv.Sniffer().sniff(csvfile.read(1024))
        csvfile.seek(0)
        reader = csv.DictReader(transaction_lines(csvfile), dialect=dialect, fieldnames=visa_field_names)

        with open("visaHomebank.csv", 'w') as outfile:
            writer = csv.DictWriter(outfile, dialect='dkb', fieldnames=homebank_field_names)
            for row in reader:
                writer.writerow(
                    {
                        'date': convert_date(row["wertstellung"]),
                        'paymode': 1,
                        'info': None,
                        'payee': None,
                        'memo': row["umsatzbeschreibung"],
                        'amount': row["betrag"],
                        'category': None,
                        'tags': None
                    })

def transaction_lines(file):
    """
    Reduce the csv lines to the lines containing actual data relevant for the conversion.

    :param file: The export CSV from DKB to be converted
    :return: The lines containing the actual transaction data
    """
    lines = file.readlines()
    i = 1
    for line in lines:
        if "Betrag" in line:
            return lines[i:]
        i = i + 1

    raise ValueError("Can't convert CSV file without header line")

def convert_date(date_string):
    date = datetime.strptime(date_string, "%d.%m.%Y")
    return date.strftime('%d-%m-%Y')

def main():
    parser = argparse.ArgumentParser(description=
                                     "Convert a CSV export file from DKB online banking "
                                     "to a Homebank compatible CSV format.")
    parser.add_argument("filename", help="The CSV file to convert.")

    group = parser.add_mutually_exclusive_group()
    group.add_argument("-v", "--visa", action="store_true", help="convert a DKB Visa account CSV file")
    group.add_argument("-c", "--cash", action="store_true", help="convert a DKB Cash account CSV file")

    args = parser.parse_args()

    if args.visa:
        convert_visa(args.filename)
        print("DKB Visa file converted. Output file: 'visaHomebank.csv'")
    elif args.cash:
        convert_DKB_cash(args.filename)
        print("DKB Cash file converted. Output file: 'cashHomebank.csv'")
    else:
        print("You must provide the type of the CSV file (--cash for DKB Cash, --visa for DKB Visa)")

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


<commit_before>from streamsx.topology.topology import *
from streamsx.topology.tester import Tester
import streamsx.ec as ec

import unittest
from datetime import timedelta

import test_vers

# Tests for checkpointing.  This does not actually test that checkpoints
# can be created and destroyed.  It only tests that operators containing
# serializable callables can be run successfully with checkpointing enabled.

# Include at least one of each of these basic types, including checkpointing
# and a dillable class:
# Aggregate.
# Filter.
# FlatMap.
# ForEach
# HashAdder
# Map.
# Source.

# Class defining a source of integers from 0 to the limit, including 0 but
# excluding the limit.
# An instance of this class can be dilled.
class TimeCounter(object):
    """Count up from zero every `period` seconds for a given number of
    iterations."""
    def __init__(self, period=None, iterations=None):
        if period is None:
            period = 1.0

        self.period = period
        self.iterations = iterations
        self.count = 0

    def __iter__(self):
        return self

    def __next__(self):
        # If the number of iterations has been met, stop iterating.
        if self.iterations is not None and self.count >= self.iterations:
            raise StopIteration

        # Otherwise increment, sleep, and return.
        to_return = self.count
        self.count += 1
        time.sleep(self.period)
        return to_return

    def next(self):
        return self.__next__()

# This just provides a __call__ method that computes the average of its
# parameter, but since it is a class it is stateful and will have its state
# saved during checkpointing.
class StatefulAverage(object):
    def __call__(self, x):
        return float(sum(x))/float(len(x))

# Pass only even integers.  This is a class, therefore is checkpointed
# even though it does not have meaningful state.
class StatefulEvenFilter(object):
    def __call__(self, x):
        return x % 2 == 0

# Map x -> x / 2 + 1.  Again this is stateful although it does not have
# meaningful state.
class StatefulHalfPlusOne(object):
    def __call__(self, x):
        return x / 2 + 1

# Split a string.
class StatefulSplit(object):
    def __call__(self, x):
        return x.split()

# Delay.  Used as a filter, but this just delays for a period and returns
# True
class StatefulDelay(object):
    def __init__(self, period):
        self.period = period

    def __call__(self, x):
        time.sleep(self.period)
        return True

# Do nothing, statefully.  This is used with for_each.  It seems not so
# easy to test for_each.
class StatefulNothing(object):
    def __call__(self, x):
        pass

# Compute a hash code, statefully.  Again the state is not meaningful
class StatefulStupidHash(object):
    def __call__(self, x):
        return hash(x) + 89

# listiterater objects cannot be pickled in python 2.7, so here is a
# list iterator class
class ListIterator(object):
    def __init__(self, listToIterate, period=None):
        self.listToIterate = listToIterate
        if period is None:
            self.period=0.1
        self.index = 0

    def __iter__(self):
        return self

    def __next__(self):
        if self.index >= len(self.listToIterate):
            raise StopIteration
        time.sleep(self.period);
        ret = self.listToIterate[self.index];
        self.index += 1
        return ret

    def next(self):
        return __next__(self)

class TestCheckpointing(unittest.TestCase):
    def setUp(self):
        Tester.setup_standalone(self)

    # Source operator
    def test_source(self):
        topo = Topology("test")
        topo.checkpoint_period = timedelta(seconds=1)
        s = topo.source(TimeCounter(iterations=30, period=0.1))
        tester = Tester(topo)
        tester.contents(s, range(0,30))

        tester.test(self.test_ctxtype, self.test_config, always_collect_logs=True)

    # Source, ForEach, Filter, Aggregate operators
    # (based on test2_python_window.TestPythonWindowing.test_basicCountCountWindow)
    def test_aggregate(self):
        topo = Topology()
        topo.checkpoint_period = timedelta(seconds=1)
        # Generate integers from [0,30)
        s = topo.source(TimeCounter(iterations=30, period=0.1))
        # Filter the odd ones
        s = s.filter(StatefulEvenFilter())
        # Halve the even ones and add one.  Now have integers [1,15)
        s = s.map(StatefulHalfPlusOne())
        s = s.last(10).trigger(2).aggregate(StatefulAverage())
        tester = Tester(topo)
        tester.contents(s, [1.5,2.5,3.5,4.5,5.5,7.5,9.5])
        tester.test(self.test_ctxtype, self.test_config)

    # Test flat map (based on a sample)
    def test_flat_map(self):
        topo = Topology();
        topo.checkpoint_period = timedelta(seconds=1)

        lines = topo.source(ListIterator(["mary had a little lamb", "its fleece was white as snow"]))
        # slow things down so checkpoints can be taken.
        lines = lines.filter(StatefulDelay(0.5))
        words = lines.flat_map(StatefulSplit())
        tester = Tester(topo)
        tester.contents(words, ["mary","had","a","little","lamb","its","fleece","was","white","as","snow"])
        tester.test(self.test_ctxtype, self.test_config)

    # Test hash adder.  This requires parallel with a hash router.
    # (based on test2_udp.TestUDP.test_TopologyParallelHash)
    def test_hash_adder(self):
        topo = Topology("test_hash_adder")
        topo.checkpoint_period = timedelta(seconds=1)
        s = topo.source(TimeCounter(iterations=30, period=0.1))
        width =  3
        s = s.parallel(width, Routing.HASH_PARTITIONED, StatefulStupidHash())
        s = s.map(lambda x: x + 23)
        s = s.end_parallel()

        expected = []
        for v in range (0,30):
            expected.append((v + 23))

        tester = Tester(topo)
        tester.contents(s, expected, ordered=width==1)
        tester.test(self.test_ctxtype, self.test_config)

    # Test for_each.  This is a sink.
    def test_for_each(self):
        topo = Topology("test")
        topo.checkpoint_period = timedelta(seconds=1)
        s = topo.source(TimeCounter(iterations=30, period=0.1))
        s.for_each(StatefulNothing())
        tester = Tester(topo)
        tester.test(self.test_ctxtype, self.test_config)

class TestDistributedCheckpointing(TestCheckpointing):
    def setUp(self):
        Tester.setup_distributed(self)

class TestSasCheckpointing(TestCheckpointing):
    def setUp(self):
        Tester.setup_streaming_analytics(self, force_remote_build=True)

<commit_msg>Remove old import<commit_after>from streamsx.topology.topology import *
from streamsx.topology.tester import Tester
import streamsx.ec as ec

import unittest
from datetime import timedelta

# Tests for checkpointing.  This does not actually test that checkpoints
# can be created and destroyed.  It only tests that operators containing
# serializable callables can be run successfully with checkpointing enabled.

# Include at least one of each of these basic types, including checkpointing
# and a dillable class:
# Aggregate.
# Filter.
# FlatMap.
# ForEach
# HashAdder
# Map.
# Source.

# Class defining a source of integers from 0 to the limit, including 0 but
# excluding the limit.
# An instance of this class can be dilled.
class TimeCounter(object):
    """Count up from zero every `period` seconds for a given number of
    iterations."""
    def __init__(self, period=None, iterations=None):
        if period is None:
            period = 1.0

        self.period = period
        self.iterations = iterations
        self.count = 0

    def __iter__(self):
        return self

    def __next__(self):
        # If the number of iterations has been met, stop iterating.
        if self.iterations is not None and self.count >= self.iterations:
            raise StopIteration

        # Otherwise increment, sleep, and return.
        to_return = self.count
        self.count += 1
        time.sleep(self.period)
        return to_return

    def next(self):
        return self.__next__()

# This just provides a __call__ method that computes the average of its
# parameter, but since it is a class it is stateful and will have its state
# saved during checkpointing.
class StatefulAverage(object):
    def __call__(self, x):
        return float(sum(x))/float(len(x))

# Pass only even integers.  This is a class, therefore is checkpointed
# even though it does not have meaningful state.
class StatefulEvenFilter(object):
    def __call__(self, x):
        return x % 2 == 0

# Map x -> x / 2 + 1.  Again this is stateful although it does not have
# meaningful state.
class StatefulHalfPlusOne(object):
    def __call__(self, x):
        return x / 2 + 1

# Split a string.
class StatefulSplit(object):
    def __call__(self, x):
        return x.split()

# Delay.  Used as a filter, but this just delays for a period and returns
# True
class StatefulDelay(object):
    def __init__(self, period):
        self.period = period

    def __call__(self, x):
        time.sleep(self.period)
        return True

# Do nothing, statefully.  This is used with for_each.  It seems not so
# easy to test for_each.
class StatefulNothing(object):
    def __call__(self, x):
        pass

# Compute a hash code, statefully.  Again the state is not meaningful
class StatefulStupidHash(object):
    def __call__(self, x):
        return hash(x) + 89

# listiterater objects cannot be pickled in python 2.7, so here is a
# list iterator class
class ListIterator(object):
    def __init__(self, listToIterate, period=None):
        self.listToIterate = listToIterate
        if period is None:
            self.period=0.1
        self.index = 0

    def __iter__(self):
        return self

    def __next__(self):
        if self.index >= len(self.listToIterate):
            raise StopIteration
        time.sleep(self.period);
        ret = self.listToIterate[self.index];
        self.index += 1
        return ret

    def next(self):
        return __next__(self)

class TestCheckpointing(unittest.TestCase):
    def setUp(self):
        Tester.setup_standalone(self)

    # Source operator
    def test_source(self):
        topo = Topology("test")
        topo.checkpoint_period = timedelta(seconds=1)
        s = topo.source(TimeCounter(iterations=30, period=0.1))
        tester = Tester(topo)
        tester.contents(s, range(0,30))

        tester.test(self.test_ctxtype, self.test_config, always_collect_logs=True)

    # Source, ForEach, Filter, Aggregate operators
    # (based on test2_python_window.TestPythonWindowing.test_basicCountCountWindow)
    def test_aggregate(self):
        topo = Topology()
        topo.checkpoint_period = timedelta(seconds=1)
        # Generate integers from [0,30)
        s = topo.source(TimeCounter(iterations=30, period=0.1))
        # Filter the odd ones
        s = s.filter(StatefulEvenFilter())
        # Halve the even ones and add one.  Now have integers [1,15)
        s = s.map(StatefulHalfPlusOne())
        s = s.last(10).trigger(2).aggregate(StatefulAverage())
        tester = Tester(topo)
        tester.contents(s, [1.5,2.5,3.5,4.5,5.5,7.5,9.5])
        tester.test(self.test_ctxtype, self.test_config)

    # Test flat map (based on a sample)
    def test_flat_map(self):
        topo = Topology();
        topo.checkpoint_period = timedelta(seconds=1)

        lines = topo.source(ListIterator(["mary had a little lamb", "its fleece was white as snow"]))
        # slow things down so checkpoints can be taken.
        lines = lines.filter(StatefulDelay(0.5))
        words = lines.flat_map(StatefulSplit())
        tester = Tester(topo)
        tester.contents(words, ["mary","had","a","little","lamb","its","fleece","was","white","as","snow"])
        tester.test(self.test_ctxtype, self.test_config)

    # Test hash adder.  This requires parallel with a hash router.
    # (based on test2_udp.TestUDP.test_TopologyParallelHash)
    def test_hash_adder(self):
        topo = Topology("test_hash_adder")
        topo.checkpoint_period = timedelta(seconds=1)
        s = topo.source(TimeCounter(iterations=30, period=0.1))
        width =  3
        s = s.parallel(width, Routing.HASH_PARTITIONED, StatefulStupidHash())
        s = s.map(lambda x: x + 23)
        s = s.end_parallel()

        expected = []
        for v in range (0,30):
            expected.append((v + 23))

        tester = Tester(topo)
        tester.contents(s, expected, ordered=width==1)
        tester.test(self.test_ctxtype, self.test_config)

    # Test for_each.  This is a sink.
    def test_for_each(self):
        topo = Topology("test")
        topo.checkpoint_period = timedelta(seconds=1)
        s = topo.source(TimeCounter(iterations=30, period=0.1))
        s.for_each(StatefulNothing())
        tester = Tester(topo)
        tester.test(self.test_ctxtype, self.test_config)

class TestDistributedCheckpointing(TestCheckpointing):
    def setUp(self):
        Tester.setup_distributed(self)

class TestSasCheckpointing(TestCheckpointing):
    def setUp(self):
        Tester.setup_streaming_analytics(self, force_remote_build=True)

<|endoftext|>


<commit_before># Copyright (c) 2014 Adafruit Industries
# Author: Tony DiCola
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
import Adafruit_GPIO.Platform as Platform

class RPi_PWM_Adapter(object):
	"""PWM implementation for the Raspberry Pi using the RPi.GPIO PWM library."""

	def __init__(self, rpi_gpio, mode=None):
		self.rpi_gpio = rpi_gpio
		# Suppress warnings about GPIO in use.
		rpi_gpio.setwarnings(False)
		# Set board or BCM pin numbering.
		if mode == rpi_gpio.BOARD or mode == rpi_gpio.BCM:
			rpi_gpio.setmode(mode)
		elif mode is not None:
			raise ValueError('Unexpected value for mode.  Must be BOARD or BCM.')
		else:
			# Default to BCM numbering if not told otherwise.
			rpi_gpio.setmode(rpi_gpio.BCM)
		# Store reference to each created PWM instance.
		self.pwm = {}

	def start(self, pin, dutycycle, frequency_hz=2000):
		"""Enable PWM output on specified pin.  Set to intiial percent duty cycle
		value (0.0 to 100.0) and frequency (in Hz).
		"""
		if dutycycle < 0.0 or dutycycle > 100.0:
			raise ValueError('Invalid duty cycle value, must be between 0.0 to 100.0 (inclusive).')
		# Make pin an output.
		self.rpi_gpio.setup(pin, self.rpi_gpio.OUT)
		# Create PWM instance and save a reference for later access.
		self.pwm[pin] = self.rpi_gpio.PWM(pin, frequency_hz)
		# Start the PWM at the specified duty cycle.
		self.pwm[pin].start(dutycycle)

	def set_duty_cycle(self, pin, dutycycle):
		"""Set percent duty cycle of PWM output on specified pin.  Duty cycle must
		be a value 0.0 to 100.0 (inclusive).
		"""
		if dutycycle < 0.0 or dutycycle > 100.0:
			raise ValueError('Invalid duty cycle value, must be between 0.0 to 100.0 (inclusive).')
		if pin not in self.pwm:
			raise ValueError('Pin {0} is not configured as a PWM.  Make sure to first call start for the pin.'.format(pin))
		self.pwm[pin].ChangeDutyCycle(dutycycle)

	def set_frequency(self, pin, frequency_hz):
		"""Set frequency (in Hz) of PWM output on specified pin."""
		if pin not in self.pwm:
			raise ValueError('Pin {0} is not configured as a PWM.  Make sure to first call start for the pin.'.format(pin))
		self.pwm[pin].ChangeFrequency(frequency_hz)

	def stop(self, pin):
		"""Stop PWM output on specified pin."""
		if pin not in self.pwm:
			raise ValueError('Pin {0} is not configured as a PWM.  Make sure to first call start for the pin.'.format(pin))
		self.pwm[pin].stop()
		del self.pwm[pin]

class BBIO_PWM_Adapter(object):
	"""PWM implementation for the BeagleBone Black using the Adafruit_BBIO.PWM
	library.
	"""

	def __init__(self, bbio_pwm):
		self.bbio_pwm = bbio_pwm

	def start(self, pin, dutycycle, frequency_hz=2000):
		"""Enable PWM output on specified pin.  Set to intiial percent duty cycle
		value (0.0 to 100.0) and frequency (in Hz).
		"""
		if dutycycle < 0.0 or dutycycle > 100.0:
			raise ValueError('Invalid duty cycle value, must be between 0.0 to 100.0 (inclusive).')
		self.bbio_pwm.start(pin, dutycycle, frequency_hz)

	def set_duty_cycle(self, pin, dutycycle):
		"""Set percent duty cycle of PWM output on specified pin.  Duty cycle must
		be a value 0.0 to 100.0 (inclusive).
		"""
		if dutycycle < 0.0 or dutycycle > 100.0:
			raise ValueError('Invalid duty cycle value, must be between 0.0 to 100.0 (inclusive).')
		self.bbio_pwm.set_duty_cycle(pin, dutycycle)

	def set_frequency(self, pin, frequency_hz):
		"""Set frequency (in Hz) of PWM output on specified pin."""
		self.bbio_pwm.set_frequency(pin, frequency_hz)

	def stop(self, pin):
		"""Stop PWM output on specified pin."""
		self.bbio_pwm.stop(pin)

def get_platform_pwm(**keywords):
	"""Attempt to return a PWM instance for the platform which the code is being
	executed on.  Currently supports only the Raspberry Pi using the RPi.GPIO
	library and Beaglebone Black using the Adafruit_BBIO library.  Will throw an
	exception if a PWM instance can't be created for the current platform.  The
	returned PWM object has the same interface as the RPi_PWM_Adapter and
	BBIO_PWM_Adapter classes.
	"""
	plat = Platform.platform_detect()
	if plat == Platform.RASPBERRY_PI:
		import RPi.GPIO
		return RPi_PWM_Adapter(RPi.GPIO, **keywords)
	elif plat == Platform.BEAGLEBONE_BLACK:
		import Adafruit_BBIO.PWM
		return BBIO_PWM_Adapter(Adafruit_BBIO.PWM, **keywords)
	elif plat == Platform.UNKNOWN:
		raise RuntimeError('Could not determine platform.')
<commit_msg>Added entirely untested PWM for sysfs<commit_after># Copyright (c) 2014 Adafruit Industries
# Author: Tony DiCola
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.

import io

import Adafruit_GPIO.Platform as Platform

class RPi_PWM_Adapter(object):
	"""PWM implementation for the Raspberry Pi using the RPi.GPIO PWM library."""

	def __init__(self, rpi_gpio, mode=None):
		self.rpi_gpio = rpi_gpio
		# Suppress warnings about GPIO in use.
		rpi_gpio.setwarnings(False)
		# Set board or BCM pin numbering.
		if mode == rpi_gpio.BOARD or mode == rpi_gpio.BCM:
			rpi_gpio.setmode(mode)
		elif mode is not None:
			raise ValueError('Unexpected value for mode.  Must be BOARD or BCM.')
		else:
			# Default to BCM numbering if not told otherwise.
			rpi_gpio.setmode(rpi_gpio.BCM)
		# Store reference to each created PWM instance.
		self.pwm = {}

	def start(self, pin, dutycycle, frequency_hz=2000):
		"""Enable PWM output on specified pin.  Set to intiial percent duty cycle
		value (0.0 to 100.0) and frequency (in Hz).
		"""
		if dutycycle < 0.0 or dutycycle > 100.0:
			raise ValueError('Invalid duty cycle value, must be between 0.0 to 100.0 (inclusive).')
		# Make pin an output.
		self.rpi_gpio.setup(pin, self.rpi_gpio.OUT)
		# Create PWM instance and save a reference for later access.
		self.pwm[pin] = self.rpi_gpio.PWM(pin, frequency_hz)
		# Start the PWM at the specified duty cycle.
		self.pwm[pin].start(dutycycle)

	def set_duty_cycle(self, pin, dutycycle):
		"""Set percent duty cycle of PWM output on specified pin.  Duty cycle must
		be a value 0.0 to 100.0 (inclusive).
		"""
		if dutycycle < 0.0 or dutycycle > 100.0:
			raise ValueError('Invalid duty cycle value, must be between 0.0 to 100.0 (inclusive).')
		if pin not in self.pwm:
			raise ValueError('Pin {0} is not configured as a PWM.  Make sure to first call start for the pin.'.format(pin))
		self.pwm[pin].ChangeDutyCycle(dutycycle)

	def set_frequency(self, pin, frequency_hz):
		"""Set frequency (in Hz) of PWM output on specified pin."""
		if pin not in self.pwm:
			raise ValueError('Pin {0} is not configured as a PWM.  Make sure to first call start for the pin.'.format(pin))
		self.pwm[pin].ChangeFrequency(frequency_hz)

	def stop(self, pin):
		"""Stop PWM output on specified pin."""
		if pin not in self.pwm:
			raise ValueError('Pin {0} is not configured as a PWM.  Make sure to first call start for the pin.'.format(pin))
		self.pwm[pin].stop()
		del self.pwm[pin]

class BBIO_PWM_Adapter(object):
	"""PWM implementation for the BeagleBone Black using the Adafruit_BBIO.PWM
	library.
	"""

	def __init__(self, bbio_pwm):
		self.bbio_pwm = bbio_pwm

	def start(self, pin, dutycycle, frequency_hz=2000):
		"""Enable PWM output on specified pin.  Set to intiial percent duty cycle
		value (0.0 to 100.0) and frequency (in Hz).
		"""
		if dutycycle < 0.0 or dutycycle > 100.0:
			raise ValueError('Invalid duty cycle value, must be between 0.0 to 100.0 (inclusive).')
		self.bbio_pwm.start(pin, dutycycle, frequency_hz)

	def set_duty_cycle(self, pin, dutycycle):
		"""Set percent duty cycle of PWM output on specified pin.  Duty cycle must
		be a value 0.0 to 100.0 (inclusive).
		"""
		if dutycycle < 0.0 or dutycycle > 100.0:
			raise ValueError('Invalid duty cycle value, must be between 0.0 to 100.0 (inclusive).')
		self.bbio_pwm.set_duty_cycle(pin, dutycycle)

	def set_frequency(self, pin, frequency_hz):
		"""Set frequency (in Hz) of PWM output on specified pin."""
		self.bbio_pwm.set_frequency(pin, frequency_hz)

	def stop(self, pin):
		"""Stop PWM output on specified pin."""
		self.bbio_pwm.stop(pin)

class SysFS_PWM_Adapter(object):
	"""PWM implementation using sysfs, as per https://www.kernel.org/doc/Documentation/pwm.txt"""

        # A new object per chip; use pin to mark channels within a chip
	def __init__(self, chip=0):
		self._chip = chip
                self.pwm  = {}  # keep track of freq and duty here, bcs sysfs wants period and on time.
                                # setting data here implies the pwm is live

	def start(self, pin, dutycycle, frequency_hz=2000):
		"""Enable PWM output on specified pin.  Set to intiial percent duty cycle
		value (0.0 to 100.0) and frequency (in Hz).
		"""
                try:
                        self.write_to_chip("export", "%d" % pin)
                except:
                        # probably mean's the pin's already enabled, if not we'll fail soon enough...
                        pass

                self.set_duty_cycle_and_freq(pin, dutycycle, frequency_hz)

                self.write_to_pwm(pin, "enable", "1")

	def set_duty_cycle_and_freq(self, pin, new_duty_cycle, new_freq_hz):
		if dutycycle < 0.0 or dutycycle > 100.0:
			raise ValueError('Invalid duty cycle value, must be between 0.0 to 100.0 (inclusive).')

                # Ensure there's always something there
                if pin not in self._pwm:
                    self.pwm[pin] = (0.0, 1000.0)

                (duty_cycle, freq_hz) = self.pwm[pin]

                if new_duty_cycle is not None:
                        duty_cycle = new_duty_cycle
                if new_freq_hz is not None:
                        freq_hz = new_freq_hz

                period_ns  = 1.0e9 / freq_hz
                time_on_ns = duty_cycle * period_ns / 100.0

                self.write_to_pwm(pin, "period",     "%d" % period_ns)
                self.write_to_pwm(pin, "duty_cycle", "%d" % time_on_ns)

                self.pwm[pin] = (duty_cycle, freq_hz)

	def set_duty_cycle(self, pin, dutycycle):
		"""Set percent duty cycle of PWM output on specified pin.  Duty cycle must
		be a value 0.0 to 100.0 (inclusive).
		"""
		if pin not in self._pwm:
			raise ValueError('Pin {0} is not configured as a PWM.  Make sure to first call start for the pin.'.format(pin))
                self.set_duty_cycle_and_freq(pin, duty_cycle, None)

	def set_frequency(self, pin, frequency_hz):
		"""Set frequency (in Hz) of PWM output on specified pin."""
		if pin not in self.pwm:
			raise ValueError('Pin {0} is not configured as a PWM.  Make sure to first call start for the pin.'.format(pin))
                self.set_duty_cycle_and_freq(pin, None, frequency_hz)

	def stop(self, pin):
		"""Stop PWM output on specified pin."""
		if pin not in self.pwm:
			raise ValueError('Pin {0} is not configured as a PWM.  Make sure to first call start for the pin.'.format(pin))

                self.write_to_pwm(pin, "enable", "0")

		del self.pwm[pin]

        def write_to_chip(self, file, data):
                write_to_file("/sys/class/pwm/pwmchip%d/%s" % (self._chip, file), data)

        def write_to_pin(self, pin, file, data):
                write_to_file("/sys/class/pwm/pwmchip%d/pwm%d/%s" % (self._chip, pin, file), data)

def write_to_file(file, data):
        with io.open(file, mode='wb', buffering=0) as f:
                f.write(data + "\n")

def get_platform_pwm(**keywords):
	"""Attempt to return a PWM instance for the platform which the code is being
	executed on.  Currently supports only the Raspberry Pi using the RPi.GPIO
	library and Beaglebone Black using the Adafruit_BBIO library.  Will throw an
	exception if a PWM instance can't be created for the current platform.  The
	returned PWM object has the same interface as the RPi_PWM_Adapter and
	BBIO_PWM_Adapter classes.
	"""
	plat = Platform.platform_detect()
	if plat == Platform.RASPBERRY_PI:
		import RPi.GPIO
		return RPi_PWM_Adapter(RPi.GPIO, **keywords)
	elif plat == Platform.BEAGLEBONE_BLACK:
		import Adafruit_BBIO.PWM
		return BBIO_PWM_Adapter(Adafruit_BBIO.PWM, **keywords)
	elif plat == Platform.UNKNOWN:
		raise RuntimeError('Could not determine platform.')
<|endoftext|>


<commit_before>from cowpy import cow
from flask import Flask, request, abort, make_response, Response

app = Flask(__name__)

@app.route('/cowsay/')
def cowsay():
    if not 't' in request.args and not 'text' in request.args:
        abort(make_response("Pass text with -t, otherwise I don't know what to say", 422))
    text = request.args.get('t', request.args.get('text'))
    return  Response(cow.get_cow()().milk(msg=text), mimetype='text/plain')

if __name__ == '__main__':
    app.run()

<commit_msg>update caas to handle commands<commit_after>from cowpy import cow
from flask import Flask, request, abort, make_response, Response

app = Flask(__name__)

@app.route('/cowsay/')
def cowsay():
    helpmsg = '''

        Welcom to cow-as-as-service. We support following commands:

            help
                display help info

            say TEXT
                draw a cow saying TEXT

    '''
    if not 'command' in request.args or request.args['command'] not in ('help', 'say'):
        abort(make_response("Command missing or invalid. Cow-as-a-service supportos only following commands: help, say", 422))
    command = request.args['command']
    arguments = request.args.getlist('arg')
    if command == 'help':
        return Response(helpmsg, mimetype='text/plain')
    elif command == 'say':
        if not arguments or len(arguments) > 1:
            return Response(helpmsg, mimetype='text/plain')
        else:
            return  Response(cow.get_cow()().milk(msg=arguments[0]), mimetype='text/plain')

if __name__ == '__main__':
    app.run(debug=True)

<|endoftext|>


<commit_before>import sys
print(sum(map(int, sys.argv[1:])))
<commit_msg>Add main to the script<commit_after>import sys

if __name__ == '__main__':
   print(sum(map(int, sys.argv[1:])))
<|endoftext|>