<commit_before>import string
import csv
import cStringIO
from datetime import *

from bo import *
from database.bubble import *
from database.person import *
from database.dictionary import *

class ShowBubbleList(boRequestHandler):
    def get(self, url):
        if self.authorize('bubbler'):
            limit = 30
            page = self.request.get('p', '1').strip()
            page = int(page)
            offset = ((page*limit)-limit)

            url = url + '/'
            url = url.split('/')

            id = int(url[0])
            letter = url[1]

            bubbletype = BubbleType().get_by_id(id)

            if letter:
                bubbles = db.Query(Bubble).filter('type', bubbletype.type).filter('is_deleted', False).filter('sort_estonian >=', letter).order('sort_estonian').fetch(limit = limit, offset = offset)
            else:
                bubbles = db.Query(Bubble).filter('type', bubbletype.type).filter('is_deleted', False).order('sort_estonian').fetch(limit = limit, offset = offset)

            show_scroll = True
            if len(bubbles) < limit:
                show_scroll = False

            self.view('application', 'bubble/bubble_list.html', {
                'bubbletype': bubbletype,
                'bubbles': bubbles,
                'next_page': (page + 1),
                'letter': letter,
                'show_scroll': show_scroll,
                'abc': string.ascii_lowercase,
            })

class ShowBubble(boRequestHandler):
    def get(self, id):
        if self.authorize('bubbler'):

            bubble = Bubble().get_by_id(int(id))
            ratingscales = db.Query(RatingScale).fetch(1000)

            #addable_bubbles = db.Query(Bubble).filter('__key__ !=', bubble.key()).filter('type IN', bubble.type2.allowed_subtypes).filter('is_deleted', False).fetch(1000)
            nextinline_bubbles = []
            for mb in bubble.in_bubbles:
                for mb2 in mb.bubbles:
                    if mb2.key() != bubble.key():
                        nextinline_bubbles.append(mb2)

            changeinfo = ''
            last_change = bubble.last_change
            if last_change:
                if last_change.user:
                    changer = db.Query(Person).filter('apps_username', last_change.user).get()
                    if changer:
                        changeinfo = Translate('bubble_changed_on') % {'name': changer.displayname, 'date': UtcToLocalDateTime(last_change.datetime).strftime('%d.%m.%Y %H:%M')}

            self.view(bubble.type2.displayname + ' - ' + bubble.displayname, 'bubble/bubble.html', {
                'bubble': bubble,
                'changed': changeinfo,
                'ratingscales': ratingscales,
                #'addable_bubbles': addable_bubbles,
                'nextinline_bubbles': nextinline_bubbles,
            })

    def post(self, key):
        if self.authorize('bubbler'):
            bubble = Bubble().get(key)
            field = self.request.get('field').strip()
            value = self.request.get('value').strip()
            if bubble:
                if value:
                    if field in ['name', 'description', 'badge']:
                        setattr(bubble, field, DictionaryAdd(('bubble_' + field), value))
                        if field == 'name':
                            bubble.sort_estonian = StringToSortable(value)
                    if field in ['start_datetime', 'end_datetime']:
                        setattr(bubble, field, datetime.strptime(value, '%d.%m.%Y %H:%M'))
                    if field == 'rating_scale':
                        bubble.rating_scale = db.Key(value)
                    if field == 'url':
                        bubble.url = value
                    if field == 'nextinline':
                        bubble.next_in_line = [db.Key(value)]
                else:
                    setattr(bubble, field, None)

                bubble.put()
                bubble.displayname_cache_reset()

class AddBubble(boRequestHandler):
    def get(self, type, key):
        if self.authorize('bubbler'):
            bubble = Bubble().get(key)
            if bubble:

                new = Bubble()
                new.type = type
                new.name = DictionaryAdd('bubble_name', '')
                new.start_datetime = bubble.start_datetime
                new.end_datetime = bubble.end_datetime
                new.put()

                bubble.optional_bubbles.append(new.key())
                bubble.put()

            self.redirect('/bubble/%s' % new.key().id())

class AddExistingBubble(boRequestHandler):
    def post(self, key):
        if self.authorize('bubbler'):
            bubble = Bubble().get(key)
            existing = self.request.get('key').strip()

            if bubble and existing:
                bubble.optional_bubbles = AddToList(db.Key(existing), bubble.optional_bubbles)
                bubble.put()

class DeleteBubble(boRequestHandler):
    def get(self, key):
        if self.authorize('bubbler'):
            bubble = Bubble().get(key)
            if bubble:
                if bubble.bubbles:
                    self.redirect('/bubble/%s' % key)
                else:
                    bubble.is_deleted = True
                    bubble.put()
                    if bubble.in_bubbles:
                        self.redirect('/bubble/%s' % bubble.in_bubbles[0].key().id())

class GetSeeders(boRequestHandler):
    def get(self):
        if self.authorize('bubbler'):
            query = self.request.get('query').strip()
            keys = []
            names = []
            for p in db.Query(Person).filter('forename >=', query).order('forename').order('surname').fetch(100):
                keys.append(str(p.key()))
                names.append(p.displayname)
            respond = {
                'query': query,
                'suggestions': names,
                'data': keys
            }

            self.echo_json(respond)

class AddSeeder(boRequestHandler):
    def get(self, bubble_key, person_key):
        if self.authorize('bubbler'):
            bubble = Bubble().get(bubble_key)
            person = Person().get(person_key)
            if bubble and person:
                bubble.seeders = AddToList(person.key(), bubble.seeders)
                bubble.put()
                person.seeder = AddToList(bubble.key(), person.seeder)
                person.put()

            persons = {}
            for p in bubble.seeders2:
                persons[str(p.key())] = p.displayname
            self.echo_json(persons)

class AddLeecher(boRequestHandler):
    def get(self, bubble_key, person_key):
        if self.authorize('bubbler'):
            bubble = Bubble().get(bubble_key)
            person = Person().get(person_key)
            if bubble and person:
                person.add_leecher(bubble.key())

            persons = {}
            for p in bubble.leechers2:
                persons[str(p.key())] = p.displayname
            self.echo_json(persons)

class DeleteSeeder(boRequestHandler):
    def get(self, bubble_key, person_key):
        if self.authorize('bubbler'):
            bubble = Bubble().get(bubble_key)
            person = Person().get(person_key)
            if bubble and person:
                if bubble.key() in person.seeder:
                    person.seeder.remove(bubble.key())
                person.put()
                if person.key() in bubble.seeders:
                    bubble.seeders.remove(person.key())
                bubble.put()

            persons = {}
            for p in bubble.seeders2:
                persons[str(p.key())] = p.displayname
            self.echo_json(persons)

class DeleteLeecher(boRequestHandler):
    def get(self, bubble_key, person_key):
        if self.authorize('bubbler'):
            bubble = Bubble().get(bubble_key)
            person = Person().get(person_key)
            if bubble and person:
                person.remove_leecher(bubble.key())

            persons = {}
            for p in bubble.leechers2:
                persons[str(p.key())] = p.displayname
            self.echo_json(persons)

class AddTimeslots(boRequestHandler):
    def post(self, bubble_key):
        if self.authorize('bubbler'):
            start_str = self.request.get('start').strip()
            end_str = self.request.get('end').strip()
            interval_str = self.request.get('interval').strip()

            interval = int(interval_str)
            start = datetime.strptime(start_str, '%d.%m.%Y %H:%M')
            end = datetime.strptime(end_str, '%d.%m.%Y %H:%M')
            timeslot = start
            times = []

            bubble = Bubble().get(bubble_key)

            while timeslot < end:
                new = Bubble()
                new.type = 'personal_time_slot'
                new.name = DictionaryAdd('bubble_name', bubble.displayname + ' - Aeg')
                new.start_datetime = timeslot

                timeslot = timeslot + timedelta(minutes=interval)

                new.end_datetime = timeslot
                new.put()

                bubble.optional_bubbles.append(new.key())
                bubble.put()

class SubBubblesCSV(boRequestHandler):
    def get(self, bubble_id):
        if self.authorize('bubbler'):
            bubble = Bubble().get_by_id(int(bubble_id))
            csvfile = cStringIO.StringIO()
            csvWriter = csv.writer(csvfile, quoting=csv.QUOTE_MINIMAL)

            for b in sorted(bubble.bubbles, key=lambda k: k.start_datetime):
                csvWriter.writerow([
                    b.type2.displayname.encode("utf-8"),
                    b.displayname.encode("utf-8"),
                    b.displaydate.encode("utf-8"),
                    ', '.join([p.displayname.encode("utf-8") for p in b.leechers2])
                ])

            self.header('Content-Type', 'text/csv; charset=utf-8')
            self.header('Content-Disposition', 'attachment; filename=' + bubble.displayname + '.csv')
            self.echo(csvfile.getvalue())
            csvfile.close()

def main():
    Route([
            (r'/bubbletype/(.*)', ShowBubbleList),
            (r'/bubble/add/(.*)/(.*)', AddBubble),
            (r'/bubble/add_existing/(.*)', AddExistingBubble),
            (r'/bubble/add_timeslot/(.*)', AddTimeslots),
            (r'/bubble/csv/(.*)', SubBubblesCSV),
            (r'/bubble/delete/(.*)', DeleteBubble),
            (r'/bubble/seeders', GetSeeders),
            (r'/bubble/seeder/add/(.*)/(.*)', AddSeeder),
            (r'/bubble/seeder/delete/(.*)/(.*)', DeleteSeeder),
            (r'/bubble/leecher/add/(.*)/(.*)', AddLeecher),
            (r'/bubble/leecher/delete/(.*)/(.*)', DeleteLeecher),
            (r'/bubble/(.*)', ShowBubble),
        ])

if __name__ == '__main__':
    main()<commit_msg>Fixed unicode error in subbubbles CSV filename<commit_after>import string
import csv
import cStringIO
from datetime import *

from bo import *
from database.bubble import *
from database.person import *
from database.dictionary import *

class ShowBubbleList(boRequestHandler):
    def get(self, url):
        if self.authorize('bubbler'):
            limit = 30
            page = self.request.get('p', '1').strip()
            page = int(page)
            offset = ((page*limit)-limit)

            url = url + '/'
            url = url.split('/')

            id = int(url[0])
            letter = url[1]

            bubbletype = BubbleType().get_by_id(id)

            if letter:
                bubbles = db.Query(Bubble).filter('type', bubbletype.type).filter('is_deleted', False).filter('sort_estonian >=', letter).order('sort_estonian').fetch(limit = limit, offset = offset)
            else:
                bubbles = db.Query(Bubble).filter('type', bubbletype.type).filter('is_deleted', False).order('sort_estonian').fetch(limit = limit, offset = offset)

            show_scroll = True
            if len(bubbles) < limit:
                show_scroll = False

            self.view('application', 'bubble/bubble_list.html', {
                'bubbletype': bubbletype,
                'bubbles': bubbles,
                'next_page': (page + 1),
                'letter': letter,
                'show_scroll': show_scroll,
                'abc': string.ascii_lowercase,
            })

class ShowBubble(boRequestHandler):
    def get(self, id):
        if self.authorize('bubbler'):

            bubble = Bubble().get_by_id(int(id))
            ratingscales = db.Query(RatingScale).fetch(1000)

            #addable_bubbles = db.Query(Bubble).filter('__key__ !=', bubble.key()).filter('type IN', bubble.type2.allowed_subtypes).filter('is_deleted', False).fetch(1000)
            nextinline_bubbles = []
            for mb in bubble.in_bubbles:
                for mb2 in mb.bubbles:
                    if mb2.key() != bubble.key():
                        nextinline_bubbles.append(mb2)

            changeinfo = ''
            last_change = bubble.last_change
            if last_change:
                if last_change.user:
                    changer = db.Query(Person).filter('apps_username', last_change.user).get()
                    if changer:
                        changeinfo = Translate('bubble_changed_on') % {'name': changer.displayname, 'date': UtcToLocalDateTime(last_change.datetime).strftime('%d.%m.%Y %H:%M')}

            self.view(bubble.type2.displayname + ' - ' + bubble.displayname, 'bubble/bubble.html', {
                'bubble': bubble,
                'changed': changeinfo,
                'ratingscales': ratingscales,
                #'addable_bubbles': addable_bubbles,
                'nextinline_bubbles': nextinline_bubbles,
            })

    def post(self, key):
        if self.authorize('bubbler'):
            bubble = Bubble().get(key)
            field = self.request.get('field').strip()
            value = self.request.get('value').strip()
            if bubble:
                if value:
                    if field in ['name', 'description', 'badge']:
                        setattr(bubble, field, DictionaryAdd(('bubble_' + field), value))
                        if field == 'name':
                            bubble.sort_estonian = StringToSortable(value)
                    if field in ['start_datetime', 'end_datetime']:
                        setattr(bubble, field, datetime.strptime(value, '%d.%m.%Y %H:%M'))
                    if field == 'rating_scale':
                        bubble.rating_scale = db.Key(value)
                    if field == 'url':
                        bubble.url = value
                    if field == 'nextinline':
                        bubble.next_in_line = [db.Key(value)]
                else:
                    setattr(bubble, field, None)

                bubble.put()
                bubble.displayname_cache_reset()

class AddBubble(boRequestHandler):
    def get(self, type, key):
        if self.authorize('bubbler'):
            bubble = Bubble().get(key)
            if bubble:

                new = Bubble()
                new.type = type
                new.name = DictionaryAdd('bubble_name', '')
                new.start_datetime = bubble.start_datetime
                new.end_datetime = bubble.end_datetime
                new.put()

                bubble.optional_bubbles.append(new.key())
                bubble.put()

            self.redirect('/bubble/%s' % new.key().id())

class AddExistingBubble(boRequestHandler):
    def post(self, key):
        if self.authorize('bubbler'):
            bubble = Bubble().get(key)
            existing = self.request.get('key').strip()

            if bubble and existing:
                bubble.optional_bubbles = AddToList(db.Key(existing), bubble.optional_bubbles)
                bubble.put()

class DeleteBubble(boRequestHandler):
    def get(self, key):
        if self.authorize('bubbler'):
            bubble = Bubble().get(key)
            if bubble:
                if bubble.bubbles:
                    self.redirect('/bubble/%s' % key)
                else:
                    bubble.is_deleted = True
                    bubble.put()
                    if bubble.in_bubbles:
                        self.redirect('/bubble/%s' % bubble.in_bubbles[0].key().id())

class GetSeeders(boRequestHandler):
    def get(self):
        if self.authorize('bubbler'):
            query = self.request.get('query').strip()
            keys = []
            names = []
            for p in db.Query(Person).filter('forename >=', query).order('forename').order('surname').fetch(100):
                keys.append(str(p.key()))
                names.append(p.displayname)
            respond = {
                'query': query,
                'suggestions': names,
                'data': keys
            }

            self.echo_json(respond)

class AddSeeder(boRequestHandler):
    def get(self, bubble_key, person_key):
        if self.authorize('bubbler'):
            bubble = Bubble().get(bubble_key)
            person = Person().get(person_key)
            if bubble and person:
                bubble.seeders = AddToList(person.key(), bubble.seeders)
                bubble.put()
                person.seeder = AddToList(bubble.key(), person.seeder)
                person.put()

            persons = {}
            for p in bubble.seeders2:
                persons[str(p.key())] = p.displayname
            self.echo_json(persons)

class AddLeecher(boRequestHandler):
    def get(self, bubble_key, person_key):
        if self.authorize('bubbler'):
            bubble = Bubble().get(bubble_key)
            person = Person().get(person_key)
            if bubble and person:
                person.add_leecher(bubble.key())

            persons = {}
            for p in bubble.leechers2:
                persons[str(p.key())] = p.displayname
            self.echo_json(persons)

class DeleteSeeder(boRequestHandler):
    def get(self, bubble_key, person_key):
        if self.authorize('bubbler'):
            bubble = Bubble().get(bubble_key)
            person = Person().get(person_key)
            if bubble and person:
                if bubble.key() in person.seeder:
                    person.seeder.remove(bubble.key())
                person.put()
                if person.key() in bubble.seeders:
                    bubble.seeders.remove(person.key())
                bubble.put()

            persons = {}
            for p in bubble.seeders2:
                persons[str(p.key())] = p.displayname
            self.echo_json(persons)

class DeleteLeecher(boRequestHandler):
    def get(self, bubble_key, person_key):
        if self.authorize('bubbler'):
            bubble = Bubble().get(bubble_key)
            person = Person().get(person_key)
            if bubble and person:
                person.remove_leecher(bubble.key())

            persons = {}
            for p in bubble.leechers2:
                persons[str(p.key())] = p.displayname
            self.echo_json(persons)

class AddTimeslots(boRequestHandler):
    def post(self, bubble_key):
        if self.authorize('bubbler'):
            start_str = self.request.get('start').strip()
            end_str = self.request.get('end').strip()
            interval_str = self.request.get('interval').strip()

            interval = int(interval_str)
            start = datetime.strptime(start_str, '%d.%m.%Y %H:%M')
            end = datetime.strptime(end_str, '%d.%m.%Y %H:%M')
            timeslot = start
            times = []

            bubble = Bubble().get(bubble_key)

            while timeslot < end:
                new = Bubble()
                new.type = 'personal_time_slot'
                new.name = DictionaryAdd('bubble_name', bubble.displayname + ' - Aeg')
                new.start_datetime = timeslot

                timeslot = timeslot + timedelta(minutes=interval)

                new.end_datetime = timeslot
                new.put()

                bubble.optional_bubbles.append(new.key())
                bubble.put()

class SubBubblesCSV(boRequestHandler):
    def get(self, bubble_id):
        if self.authorize('bubbler'):
            bubble = Bubble().get_by_id(int(bubble_id))
            csvfile = cStringIO.StringIO()
            csvWriter = csv.writer(csvfile, quoting=csv.QUOTE_MINIMAL)

            for b in sorted(bubble.bubbles, key=lambda k: k.start_datetime):
                csvWriter.writerow([
                    b.type2.displayname.encode("utf-8"),
                    b.displayname.encode("utf-8"),
                    b.displaydate.encode("utf-8"),
                    ', '.join([p.displayname.encode("utf-8") for p in b.leechers2])
                ])

            self.header('Content-Type', 'text/csv; charset=utf-8')
            self.header('Content-Disposition', 'attachment; filename=' + unicode(bubble.displayname.encode("utf-8"), errors='ignore') + '.csv')
            self.echo(csvfile.getvalue())
            csvfile.close()

def main():
    Route([
            (r'/bubbletype/(.*)', ShowBubbleList),
            (r'/bubble/add/(.*)/(.*)', AddBubble),
            (r'/bubble/add_existing/(.*)', AddExistingBubble),
            (r'/bubble/add_timeslot/(.*)', AddTimeslots),
            (r'/bubble/csv/(.*)', SubBubblesCSV),
            (r'/bubble/delete/(.*)', DeleteBubble),
            (r'/bubble/seeders', GetSeeders),
            (r'/bubble/seeder/add/(.*)/(.*)', AddSeeder),
            (r'/bubble/seeder/delete/(.*)/(.*)', DeleteSeeder),
            (r'/bubble/leecher/add/(.*)/(.*)', AddLeecher),
            (r'/bubble/leecher/delete/(.*)/(.*)', DeleteLeecher),
            (r'/bubble/(.*)', ShowBubble),
        ])

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


<commit_before>import gc

import pytest

import vanilla

import vanilla.http

# TODO: remove
import logging
logging.basicConfig()

class TestHTTP(object):
    def test_get_body(self):
        h = vanilla.Hub()

        serve = h.http.listen()

        @h.spawn
        def _():
            conn = serve.recv()
            for request in conn:
                request.reply(vanilla.http.Status(200), {}, request.path)

        uri = 'http://localhost:%s' % serve.port
        conn = h.http.connect(uri)

        response = conn.get('/')
        response = response.recv()
        assert response.status.code == 200
        assert response.consume() == '/'

        response = conn.get('/toby').recv()
        assert response.status.code == 200
        assert response.consume() == '/toby'
        h.stop()
        assert not h.registered

    def test_get_chunked(self):
        h = vanilla.Hub()

        serve = h.http.listen()

        @h.spawn
        def _():
            conn = serve.recv()
            for request in conn:
                sender, recver = h.pipe()
                request.reply(vanilla.http.Status(200), {}, recver)

                for i in xrange(3):
                    h.sleep(10)
                    sender.send(str(i))

                if len(request.path) > 1:
                    sender.send(request.path[1:])

                sender.close()

        uri = 'http://localhost:%s' % serve.port
        conn = h.http.connect(uri)

        response = conn.get('/').recv()
        assert response.status.code == 200
        assert list(response.body) == ['0', '1', '2']

        response = conn.get('/peace').recv()
        assert response.status.code == 200
        assert list(response.body) == ['0', '1', '2', 'peace']
        h.stop()
        assert not h.registered

    def test_post(self):
        h = vanilla.Hub()

        serve = h.http.listen()

        @h.spawn
        def _():
            conn = serve.recv()
            for request in conn:
                request.reply(vanilla.http.Status(200), {}, request.consume())

        uri = 'http://localhost:%s' % serve.port
        conn = h.http.connect(uri)

        response = conn.post('/').recv()
        assert response.status.code == 200
        assert response.consume() == ''

        response = conn.post('/', data='toby').recv()
        assert response.status.code == 200
        assert response.consume() == 'toby'
        h.stop()

    def test_put(self):
        h = vanilla.Hub()

        serve = h.http.listen()

        @h.spawn
        def _():
            conn = serve.recv()
            for request in conn:
                request.reply(
                    vanilla.http.Status(200),
                    {},
                    request.method+request.consume())

        uri = 'http://localhost:%s' % serve.port
        conn = h.http.connect(uri)

        response = conn.put('/').recv()
        assert response.status.code == 200
        assert response.consume() == 'PUT'

        response = conn.put('/', data='toby').recv()
        assert response.status.code == 200
        assert response.consume() == 'PUTtoby'
        h.stop()

    def test_delete(self):
        h = vanilla.Hub()

        serve = h.http.listen()

        @h.spawn
        def _():
            conn = serve.recv()
            for request in conn:
                request.reply(vanilla.http.Status(200), {}, request.method)

        uri = 'http://localhost:%s' % serve.port
        conn = h.http.connect(uri)

        response = conn.delete('/').recv()
        assert response.status.code == 200
        assert response.consume() == 'DELETE'
        h.stop()

    def test_404(self):
        h = vanilla.Hub()

        serve = h.http.listen()

        @h.spawn
        def _():
            conn = serve.recv()
            for request in conn:
                request.reply(vanilla.http.Status(404), {}, '')

        uri = 'http://localhost:%s' % serve.port
        response = h.http.connect(uri).get('/').recv()
        assert response.status.code == 404
        h.stop()

    def test_overlap(self):
        h = vanilla.Hub()

        serve = h.http.listen()

        @h.spawn
        def _():
            conn = serve.recv()
            for request in conn:
                t = request.path[1:]
                h.sleep(int(t))
                request.reply(vanilla.http.Status(200), {}, t)

        uri = 'http://localhost:%s' % serve.port
        conn = h.http.connect(uri)

        q = h.queue(10)

        def go(t):
            r = conn.get('/'+str(t)).recv()
            q.send(int(r.consume()))

        h.spawn(go, 50)
        h.spawn(go, 20)

        assert q.recv() == 50
        assert q.recv() == 20
        h.stop()

    def test_basic_auth(self):
        h = vanilla.Hub()

        serve = h.http.listen()

        @h.spawn
        def _():
            conn = serve.recv()
            for request in conn:
                request.reply(
                    vanilla.http.Status(200),
                    {},
                    request.headers['Authorization'])

        uri = 'http://localhost:%s' % serve.port
        conn = h.http.connect(uri)

        response = conn.get('/', auth=('foo', 'bar'))
        response = response.recv()
        assert response.consume() == 'Basic Zm9vOmJhcg=='

    def test_connection_lost(self):
        h = vanilla.Hub()

        serve = h.http.listen()

        @h.spawn
        def _():
            conn = serve.recv()
            for request in conn:
                conn.socket.close()
                request.reply(vanilla.http.Status(200), {}, '.')

        uri = 'http://localhost:%s' % serve.port
        conn = h.http.connect(uri)

        response = conn.get('/')
        pytest.raises(vanilla.ConnectionLost, response.recv)
        h.stop()

class TestWebsocket(object):
    def test_websocket(self):
        h = vanilla.Hub()

        serve = h.http.listen()

        @h.spawn
        def _():
            conn = serve.recv()
            request = conn.recv()
            ws = request.upgrade()
            for item in ws.recver:
                ws.send(item)

        uri = 'ws://localhost:%s' % serve.port
        ws = h.http.connect(uri).websocket('/')

        gc.collect()

        message = 'x' * 125
        ws.send(message)
        assert ws.recv() == message

        message = 'x' * 126
        ws.send(message)
        assert ws.recv() == message

        message = 'x' * 65535
        ws.send(message)
        assert ws.recv() == message

        message = 'x' * 65536
        ws.send(message)
        assert ws.recv() == message

        # test we can call select on the websocket
        message = 'x' * 125
        ws.send(message)
        assert h.select([ws.recver]) == (ws.recver, message)
        h.stop()

    def test_websocket_end(self):
        h = vanilla.Hub()

        serve = h.http.listen()

        @h.spawn
        def _():
            conn = serve.recv()
            request = conn.recv()
            ws = request.upgrade()
            # TODO: test close while a request is still pending
            # looks to be a bug in HTTPClient
            # ws.recv()
            ws.close()

        uri = 'ws://localhost:%s' % serve.port
        ws = h.http.connect(uri).websocket('/')
        ws.send('1')
        pytest.raises(vanilla.Closed, ws.recv)
        h.stop()
<commit_msg>actually, it's fine .. just a timing issue which I'm not sure we care about<commit_after>import gc

import pytest

import vanilla

import vanilla.http

# TODO: remove
import logging
logging.basicConfig()

class TestHTTP(object):
    def test_get_body(self):
        h = vanilla.Hub()

        serve = h.http.listen()

        @h.spawn
        def _():
            conn = serve.recv()
            for request in conn:
                request.reply(vanilla.http.Status(200), {}, request.path)

        uri = 'http://localhost:%s' % serve.port
        conn = h.http.connect(uri)

        response = conn.get('/')
        response = response.recv()
        assert response.status.code == 200
        assert response.consume() == '/'

        response = conn.get('/toby').recv()
        assert response.status.code == 200
        assert response.consume() == '/toby'
        h.stop()
        assert not h.registered

    def test_get_chunked(self):
        h = vanilla.Hub()

        serve = h.http.listen()

        @h.spawn
        def _():
            conn = serve.recv()
            for request in conn:
                sender, recver = h.pipe()
                request.reply(vanilla.http.Status(200), {}, recver)

                for i in xrange(3):
                    h.sleep(10)
                    sender.send(str(i))

                if len(request.path) > 1:
                    sender.send(request.path[1:])

                sender.close()

        uri = 'http://localhost:%s' % serve.port
        conn = h.http.connect(uri)

        response = conn.get('/').recv()
        assert response.status.code == 200
        assert list(response.body) == ['0', '1', '2']

        response = conn.get('/peace').recv()
        assert response.status.code == 200
        assert list(response.body) == ['0', '1', '2', 'peace']
        h.stop()
        assert not h.registered

    def test_post(self):
        h = vanilla.Hub()

        serve = h.http.listen()

        @h.spawn
        def _():
            conn = serve.recv()
            for request in conn:
                request.reply(vanilla.http.Status(200), {}, request.consume())

        uri = 'http://localhost:%s' % serve.port
        conn = h.http.connect(uri)

        response = conn.post('/').recv()
        assert response.status.code == 200
        assert response.consume() == ''

        response = conn.post('/', data='toby').recv()
        assert response.status.code == 200
        assert response.consume() == 'toby'
        h.stop()

    def test_put(self):
        h = vanilla.Hub()

        serve = h.http.listen()

        @h.spawn
        def _():
            conn = serve.recv()
            for request in conn:
                request.reply(
                    vanilla.http.Status(200),
                    {},
                    request.method+request.consume())

        uri = 'http://localhost:%s' % serve.port
        conn = h.http.connect(uri)

        response = conn.put('/').recv()
        assert response.status.code == 200
        assert response.consume() == 'PUT'

        response = conn.put('/', data='toby').recv()
        assert response.status.code == 200
        assert response.consume() == 'PUTtoby'
        h.stop()

    def test_delete(self):
        h = vanilla.Hub()

        serve = h.http.listen()

        @h.spawn
        def _():
            conn = serve.recv()
            for request in conn:
                request.reply(vanilla.http.Status(200), {}, request.method)

        uri = 'http://localhost:%s' % serve.port
        conn = h.http.connect(uri)

        response = conn.delete('/').recv()
        assert response.status.code == 200
        assert response.consume() == 'DELETE'
        h.stop()

    def test_404(self):
        h = vanilla.Hub()

        serve = h.http.listen()

        @h.spawn
        def _():
            conn = serve.recv()
            for request in conn:
                request.reply(vanilla.http.Status(404), {}, '')

        uri = 'http://localhost:%s' % serve.port
        response = h.http.connect(uri).get('/').recv()
        assert response.status.code == 404
        h.stop()

    def test_overlap(self):
        h = vanilla.Hub()

        serve = h.http.listen()

        @h.spawn
        def _():
            conn = serve.recv()
            for request in conn:
                t = request.path[1:]
                h.sleep(int(t))
                request.reply(vanilla.http.Status(200), {}, t)

        uri = 'http://localhost:%s' % serve.port
        conn = h.http.connect(uri)

        q = h.queue(10)

        def go(t):
            r = conn.get('/'+str(t)).recv()
            q.send(int(r.consume()))

        h.spawn(go, 50)
        h.spawn(go, 20)

        assert q.recv() == 50
        assert q.recv() == 20
        h.stop()

    def test_basic_auth(self):
        h = vanilla.Hub()

        serve = h.http.listen()

        @h.spawn
        def _():
            conn = serve.recv()
            for request in conn:
                request.reply(
                    vanilla.http.Status(200),
                    {},
                    request.headers['Authorization'])

        uri = 'http://localhost:%s' % serve.port
        conn = h.http.connect(uri)

        response = conn.get('/', auth=('foo', 'bar'))
        response = response.recv()
        assert response.consume() == 'Basic Zm9vOmJhcg=='

    def test_connection_lost(self):
        h = vanilla.Hub()

        serve = h.http.listen()

        @h.spawn
        def _():
            conn = serve.recv()
            for request in conn:
                conn.socket.close()

        uri = 'http://localhost:%s' % serve.port
        conn = h.http.connect(uri)

        response = conn.get('/')
        pytest.raises(vanilla.ConnectionLost, response.recv)
        h.stop()

class TestWebsocket(object):
    def test_websocket(self):
        h = vanilla.Hub()

        serve = h.http.listen()

        @h.spawn
        def _():
            conn = serve.recv()
            request = conn.recv()
            ws = request.upgrade()
            for item in ws.recver:
                ws.send(item)

        uri = 'ws://localhost:%s' % serve.port
        ws = h.http.connect(uri).websocket('/')

        gc.collect()

        message = 'x' * 125
        ws.send(message)
        assert ws.recv() == message

        message = 'x' * 126
        ws.send(message)
        assert ws.recv() == message

        message = 'x' * 65535
        ws.send(message)
        assert ws.recv() == message

        message = 'x' * 65536
        ws.send(message)
        assert ws.recv() == message

        # test we can call select on the websocket
        message = 'x' * 125
        ws.send(message)
        assert h.select([ws.recver]) == (ws.recver, message)
        h.stop()

    def test_websocket_end(self):
        h = vanilla.Hub()

        serve = h.http.listen()

        @h.spawn
        def _():
            conn = serve.recv()
            request = conn.recv()
            ws = request.upgrade()
            ws.recv()
            ws.close()

        uri = 'ws://localhost:%s' % serve.port
        ws = h.http.connect(uri).websocket('/')
        ws.send('1')
        pytest.raises(vanilla.Closed, ws.recv)
        h.stop()
<|endoftext|>


<commit_before>from arpeggio import PTNodeVisitor

def pass_visitor(num=0):
    def visitor(a, node, children):
        return children[num]
    return visitor

def collect_visitor(a, node, children):
    return ''.join(children)

def constant_visitor(const):
    def visitor(a, node, children):
        return const
    return visitor

def constant_add_visitor(const, num=0):
    def visitor(a, node, children):
        children[num].update(const)
        return children[num]
    return visitor

def dict_visitor(key):
    def visitor(a, node, children):
        return { key: ''.join(children) }
    return visitor

def dict_combine_visitor(a, node, children):
    result = {}
    for child in children:
        if isinstance(child, dict): result.update(child)
    return result

def dict_append_visitor(to_append):
    def visitor(a, node, children):
        result = dict_combine_visitor(a, node, children)
        result.update(to_append)
        return result
    return visitor

class IthkuilVisitor(PTNodeVisitor):

    visit_consonant = pass_visitor()

    visit_vowel = pass_visitor()

    visit_consonants = collect_visitor

    visit_vowels = collect_visitor

    visit_formative = dict_append_visitor({ 'type': 'formative' })

    visit_stress_formative = pass_visitor()

    visit_penultimate_stress_formative = constant_add_visitor({ 'stress': -2 })

    visit_ultimate_stress_formative = constant_add_visitor({ 'stress': -1 })

    visit_antepenultimate_stress_formative = constant_add_visitor({ 'stress': -3 })

    visit_preantepenultimate_stress_formative = constant_add_visitor({ 'stress': -4 })

    visit_main_formative = dict_combine_visitor

    visit_prefix = dict_combine_visitor

    visit_prefix_no_cv_vl = dict_combine_visitor

    visit_vr = dict_visitor('Vr')

    visit_cg = dict_visitor('Cg')

    def visit_cs(self, node, children):
        children.insert(1, '-')
        return dict_visitor('Cs')(self, node, children)

    visit_vl = dict_visitor('Vl')

    visit_cv = dict_visitor('Cv')

    visit_root_part_we = dict_combine_visitor

    visit_root_part = dict_combine_visitor

    visit_vc = dict_visitor('Vc')

    visit_civi = dict_visitor('Ci+Vi')

    visit_ca = dict_visitor('Ca')

    visit_has_format = dict_combine_visitor

    visit_root = dict_visitor('Cr')

    def visit_incorporated_root(self, node, children): return { 'Cx': children[0]['Cr'], 'Vp': children[1] }

    visit_vf_no_format = dict_visitor('Vf')

    visit_vf = dict_visitor('Vf')

    visit_vf_format = pass_visitor()

    visit_suffix_fe_type = pass_visitor()

    def visit_suffix_format_exp(self, node, children): return { 'type': children[1], 'degree': children[0] }

    def visit_suffix(self, node, children): return { 'type': children[1], 'degree': children[0] }

    def visit_suffixes(self, node, children): return { 'VxC': children }

    def visit_suffixes_fe(self, node, children): return { 'VxC': children }

    def visit_stop(self, node, children): return '’'

    visit_geminable = pass_visitor()

    visit_consonant4 = pass_visitor()

    visit_tone = dict_visitor('tone')

    visit_cb = dict_visitor('Cb')

    visit_bias = pass_visitor(1)

    visit_validation = pass_visitor()

    # vebal adjuncts

    visit_verbal_adjunct = dict_append_visitor({ 'type': 'verbal adjunct' })

    visit_cl = dict_visitor('Cl')

    visit_vs = dict_visitor('Vs')

    visit_ve = dict_visitor('Ve')

    visit_vm = dict_visitor('Vm')

    visit_word = pass_visitor()

    # personal adjuncts

    visit_personal_adjunct = constant_add_visitor({ 'type': 'personal adjunct' })

    visit_single_referent_penultimate = constant_add_visitor({ 'stress': -2 })

    visit_single_referent_ultimate = constant_add_visitor({ 'stress': -1 })

    visit_single_referent = dict_combine_visitor

    visit_short_form = dict_combine_visitor

    visit_long_form = dict_combine_visitor

    visit_c1 = dict_visitor('C1')

    visit_vcp = collect_visitor

    visit_vcp1 = dict_visitor('Vc')

    visit_vcp2 = dict_visitor('Vc2')

    visit_cz = dict_visitor('Cz')

    visit_vz = dict_visitor('Vz')

    visit_csp = collect_visitor

    visit_vsp = collect_visitor

    def visit_rev_suffix(self, node, children):
        return { 'type': children[0], 'degree': children[1] }

    def visit_rev_suffixes(self, node, children):
        return { 'VxC': children }

    def visit_conjunct_form(self, node, children):
        if len(children) == 2:
            if 'VxC' in children[1]:
                children[1]['VxC'].append(children[0])
            else:
                children[1]['VxC'] = [children[0]]
            return children[1]
        else:
            return children[0]

    visit_collapsed_form = dict_combine_visitor

    visit_high_tone = dict_visitor('tone')

    visit_four_tone = dict_visitor('tone')

    visit_four_tone_single = dict_visitor('tone')

    visit_dual_referent_penultimate = constant_add_visitor({ 'stress' : -2 })

    visit_dual_referent_ultimate = constant_add_visitor({ 'stress' : -1 })

    visit_dual_referent_antepenultimate = constant_add_visitor({ 'stress' : -3 })

    visit_dual_referent_preantepenultimate = constant_add_visitor({ 'stress' : -4 })

    visit_dual_referent = dict_combine_visitor

    visit_vw = dict_visitor('Vw')

    visit_ck = dict_visitor('Ck')

    visit_c2 = dict_visitor('C2')

    def visit_aspectual_adjunct(self, node, children):
        return { 'type': 'aspectual adjunct', 'Vs': ''.join(children) }

    def visit_affixual_adjunct(self, node, children):
        return { 'type': 'affixual adjunct', 'VxC': { 'type': children[1], 'degree': ''.join(children[:-1]) } }

    visit_bias_adjunct = dict_append_visitor({ 'type': 'bias adjunct' })
<commit_msg>Added comments to the visitor methods<commit_after>from arpeggio import PTNodeVisitor

def pass_visitor(num=0):
    '''A visiting function that will pass the selected child unchanged
    (by default: the first one)'''
    def visitor(a, node, children):
        return children[num]
    return visitor

def collect_visitor(a, node, children):
    '''A visiting function that joins all children into one string'''
    return ''.join(children)

def constant_visitor(const):
    '''A visiting function that returns a constant'''
    def visitor(a, node, children):
        return const
    return visitor

def constant_add_visitor(const, num=0):
    '''A visiting function that extends one of the child dicts with a constant dict'''
    def visitor(a, node, children):
        children[num].update(const)
        return children[num]
    return visitor

def dict_visitor(key):
    '''A visiting function that joins the children into a string and returns it as a value in a dict'''
    def visitor(a, node, children):
        return { key: ''.join(children) }
    return visitor

def dict_combine_visitor(a, node, children):
    '''A visiting function that combines child dicts into one'''
    result = {}
    for child in children:
        if isinstance(child, dict): result.update(child)
    return result

def dict_append_visitor(to_append):
    '''A visiting function that combines child dicts into one and extends it'''
    def visitor(a, node, children):
        result = dict_combine_visitor(a, node, children)
        result.update(to_append)
        return result
    return visitor

class IthkuilVisitor(PTNodeVisitor):
    ''' A visitor class that generates a dict of all slots present in the word with some additional info'''

    # do nothing with a recognized word, the dict is already generated
    visit_word = pass_visitor()

    ### Formatives

    # do nothing with single consonants
    visit_consonant = pass_visitor()

    # do nothing with single vowels
    visit_vowel = pass_visitor()

    # combine consonant blocks
    visit_consonants = collect_visitor

    # combine vocalic blocks
    visit_vowels = collect_visitor

    # do nothing with "geminable" consonants
    visit_geminable = pass_visitor()

    # do nothing with consonants belonging to the set of four
    visit_consonant4 = pass_visitor()

    # tone goes directly into the dict as a slot
    visit_tone = dict_visitor('tone')

    # for a glottal stop just return the character
    def visit_stop(self, node, children): return '’'

    # add Cv slot to the dict
    visit_cv = dict_visitor('Cv')

    # add Vl slot to the dict
    visit_vl = dict_visitor('Vl')

    # '-' gets lost in parsing - reinsert it and add Cs slot to the dict
    def visit_cs(self, node, children):
        children.insert(1, '-')
        return dict_visitor('Cs')(self, node, children)

    # do nothing with the recognized valid consonantal form
    visit_validation = pass_visitor()

    # add Cg slot to the dict
    visit_cg = dict_visitor('Cg')

    # combine the optional prefix into a single dict
    visit_prefix = dict_combine_visitor
    visit_prefix_no_cv_vl = dict_combine_visitor

    # add Vr slot to the dict
    visit_vr = dict_visitor('Vr')

    # root goes into the Cr slot
    visit_root = dict_visitor('Cr')

    # incorporated root uses the rule for root - so it gets the root as Cr slot -> redefine to Cx and add Vp
    def visit_incorporated_root(self, node, children): return { 'Cx': children[0]['Cr'], 'Vp': children[1] }

    # add Vc slot
    visit_vc = dict_visitor('Vc')

    # add Ci+Vi slot
    visit_civi = dict_visitor('Ci+Vi')

    # add Ca slot
    visit_ca = dict_visitor('Ca')

    # both Vf and Vf without format belong to the Vf slot
    visit_vf_no_format = dict_visitor('Vf')
    visit_vf = dict_visitor('Vf')

    # Vf with format uses another rule, so the dict is already created - do nothing
    visit_vf_format = pass_visitor()

    # deal with a single suffix - create a dict with the type and degree
    def visit_suffix_format_exp(self, node, children): return { 'type': children[1], 'degree': children[0] }
    def visit_suffix(self, node, children): return { 'type': children[1], 'degree': children[0] }

    # do nothing with the recognized format expansion suffix - other rules will take care of it
    visit_suffix_fe_type = pass_visitor()

    # combine a list of suffixes into a list in the VxC slot
    def visit_suffixes(self, node, children): return { 'VxC': children }
    def visit_suffixes_fe(self, node, children): return { 'VxC': children }

    # combine all the parts that define the format into a single dict
    visit_has_format = dict_combine_visitor

    # combine the central part of the formative
    visit_root_part_we = dict_combine_visitor
    visit_root_part = dict_combine_visitor

    # combine everything except tone and bias
    visit_main_formative = dict_combine_visitor

    # stress recognition - add an appropriate key to the dict
    visit_penultimate_stress_formative = constant_add_visitor({ 'stress': -2 })
    visit_ultimate_stress_formative = constant_add_visitor({ 'stress': -1 })
    visit_antepenultimate_stress_formative = constant_add_visitor({ 'stress': -3 })
    visit_preantepenultimate_stress_formative = constant_add_visitor({ 'stress': -4 })

    # the stress is already recognized - do nothing
    visit_stress_formative = pass_visitor()

    # recognize intensified bias
    def visit_cb(self, node, children):
        block = ''.join(children)
        cb = ""
        plus = False
        if len(block) == 2 and block[0] == block[1]:
            cb = block[0:1]
            plus = True
        elif len(block) == 3 and block[0] == block[1]:
            cb = block[1:]
            plus = True
        elif len(block) == 3 and block[1] == block[2]:
            cb = block[0:2]
            plus = True
        else:
            cb = block
        return { 'Cb': cb, 'Cb+': plus }

    # omit the glottal stop when passing the bias part
    visit_bias = pass_visitor(1)

    # append word type information to a recognized formative
    visit_formative = dict_append_visitor({ 'type': 'formative' })

    ### vebal adjuncts

    # add Cl slot to the dict
    visit_cl = dict_visitor('Cl')

    # add Vs slot to the dict
    visit_vs = dict_visitor('Vs')

    # add Ve slot to the dict
    visit_ve = dict_visitor('Ve')

    # add Vm slot to the dict
    visit_vm = dict_visitor('Vm')

    # append word type information
    visit_verbal_adjunct = dict_append_visitor({ 'type': 'verbal adjunct' })

    ### personal adjuncts

    # add C1 slot to the dict
    visit_c1 = dict_visitor('C1')

    # collect the case slot vowels
    visit_vcp = collect_visitor

    # add Vc slot to the dict
    visit_vcp1 = dict_visitor('Vc')

    # add Vc2 slot to the dict
    visit_vcp2 = dict_visitor('Vc2')

    # add Cz slot
    visit_cz = dict_visitor('Cz')

    # add Vz slot
    visit_vz = dict_visitor('Vz')

    # collect suffix consonants
    visit_csp = collect_visitor

    # collect suffix vowels
    visit_vsp = collect_visitor

    # combine slots into suffix info
    def visit_rev_suffix(self, node, children):
        return { 'type': children[0], 'degree': children[1] }

    # collect all suffixes
    def visit_rev_suffixes(self, node, children):
        return { 'VxC': children }

    # collect recognized short form information
    visit_short_form = dict_combine_visitor

    # collect recognized long form information
    visit_long_form = dict_combine_visitor

    # collect recognized conjunct form information
    def visit_conjunct_form(self, node, children):
        if len(children) == 2:
            if 'VxC' in children[1]:
                children[1]['VxC'].append(children[0])
            else:
                children[1]['VxC'] = [children[0]]
            return children[1]
        else:
            return children[0]

    # collect recognized collapsed form information
    visit_collapsed_form = dict_combine_visitor

    # collect single referent adjunct info
    visit_single_referent = dict_combine_visitor

    # recognize stress
    visit_single_referent_penultimate = constant_add_visitor({ 'stress': -2 })
    visit_single_referent_ultimate = constant_add_visitor({ 'stress': -1 })

    # add tone slot
    visit_high_tone = dict_visitor('tone')
    visit_four_tone = dict_visitor('tone')
    visit_four_tone_single = dict_visitor('tone')

    # add Vw slot to the dict
    visit_vw = dict_visitor('Vw')

    # add Ck slot to the dict
    visit_ck = dict_visitor('Ck')

    # add C2 slot to the dict
    visit_c2 = dict_visitor('C2')

    # collect dual referent adjunct info
    visit_dual_referent = dict_combine_visitor

    # recognize stress
    visit_dual_referent_penultimate = constant_add_visitor({ 'stress' : -2 })
    visit_dual_referent_ultimate = constant_add_visitor({ 'stress' : -1 })
    visit_dual_referent_antepenultimate = constant_add_visitor({ 'stress' : -3 })
    visit_dual_referent_preantepenultimate = constant_add_visitor({ 'stress' : -4 })

    # add word type info
    visit_personal_adjunct = constant_add_visitor({ 'type': 'personal adjunct' })

    # handle aspectual adjuncts
    def visit_aspectual_adjunct(self, node, children):
        return { 'type': 'aspectual adjunct', 'Vs': ''.join(children) }

    # handle affixual adjuncts
    def visit_affixual_adjunct(self, node, children):
        return { 'type': 'affixual adjunct', 'VxC': { 'type': children[1], 'degree': ''.join(children[:-1]) } }

    # handle bias adjuncts
    visit_bias_adjunct = dict_append_visitor({ 'type': 'bias adjunct' })
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
"""Control and configure a JACK server via DBus."""

import logging

from functools import partial

import dbus

log = logging.getLogger(__name__)
SETTINGS = {
    'engine': (
        'driver',
        'realtime',
        ('realtime-priority', dbus.Int32),
        'port-max',
        'verbose',
        ('client-timeout', dbus.Int32),
    ),
    'driver': (
        'capture',
        'playback',
        'device',
        'rate',
        'period',
        'nperiods',
        'outchannels',
        'inchannels',
        ('channels', dbus.Int32),
        'midi',
        'hwmon',
        'hwmeter',
        'shorts',
        'softmode',
    )
}

def get_jack_controller(bus=None):
    if not bus:
        bus = dbus.SessionBus()
    return bus.get_object("org.jackaudio.service", "/org/jackaudio/Controller")

class JackBaseInterface:
    def __init__(self, jackctl=None):
        if not jackctl:
            jackctl = get_jack_controller()

        self._if = dbus.Interface(jackctl, self.interface)

    def _async_handler(self, *args, **kw):
        name = kw.get('name')
        callback = kw.get('callback')

        if args and isinstance(args[0], dbus.DBusException):
            log.error("Async call failed name=%s: %s", name, args[0])
            return

        if callback:
            callback(*args, name=name)

    def call_async(self, meth, name, callback=None, *args):
        if callback:
            handler = partial(self._async_handler, callback=callback, name=name)
            kw = dict(reply_handler=handler, error_handler=handler)
        else:
            kw = {}
        return getattr(self._if, meth)(*args, **kw)

class JackCtlInterface(JackBaseInterface):
    interface = "org.jackaudio.JackControl"

    def is_started(self, cb=None):
        return self.call_async('IsStarted', 'is_started', cb)

    def is_realtime(self, cb=None):
        return self.call_async('IsRealtime', 'is_realtime', cb)

    def start_server(self, cb=None):
        return self.call_async('StartServer', 'start_server', cb)

    def stop_server(self, cb=None):
        return self.call_async('StopServer', 'stop_server', cb)

    def get_latency(self, cb=None):
        return self.call_async('GetLatency', 'latency', cb)

    def get_load(self, cb=None):
        return self.call_async('GetLoad', 'load', cb)

    def get_period(self, cb=None):
        return self.call_async('GetBufferSize', 'period', cb)

    def get_sample_rate(self, cb=None):
        return self.call_async('GetSampleRate', 'samplerate', cb)

    def get_xruns(self, cb=None):
        return self.call_async('GetXruns', 'xruns', cb)

class JackCfgInterface(JackBaseInterface):
    interface = "org.jackaudio.Configure"

    def engine_has_feature(self, feature):
        try:
            features = self._if.ReadContainer(["engine"])[1]
        except:
            features = ()
        return dbus.String(feature) in features

    def get_engine_parameter(self, parameter, fallback=None):
        if not self.engine_has_feature(parameter):
            return fallback
        else:
            try:
                return self._if.GetParameterValue(["engine", parameter])[2]
            except:
                return fallback

    def set_engine_parameter(self, parameter, value, optional=True):
        if not self.engine_has_feature(parameter):
            return False
        elif optional:
            pvalue = self._if.GetParameterValue(["engine", parameter])

            if pvalue is None:
                return False

            if value != pvalue[2]:
                return bool(self._if.SetParameterValue(["engine", parameter],
                                                       value))
            else:
                return False
        else:
            return bool(self._if.SetParameterValue(["engine", parameter],
                                                   value))

    def driver_has_feature(self, feature):
        try:
            features = self._if.ReadContainer(["driver"])[1]
        except:
            features = ()
        return dbus.String(feature) in features

    def get_driver_parameter(self, parameter, fallback=None):
        if not self.driver_has_feature(parameter):
            return fallback
        else:
            try:
                return self._if.GetParameterValue(["driver", parameter])[2]
            except:
                return fallback

    def set_driver_parameter(self, parameter, value, optional=True):
        if not self.driver_has_feature(parameter):
            return False
        elif optional:
            if value != self._if.GetParameterValue(["driver", parameter])[2]:
                return bool(self._if.SetParameterValue(["driver", parameter],
                                                       value))
            else:
                return False
        else:
            return bool(self._if.SetParameterValue(["driver", parameter],
                                                   value))

    def activate_preset(self, settings):
        for component in ('engine', 'driver'):
            csettings = settings.get(component, {})

            for setting in SETTINGS[component]:
                if isinstance(setting, tuple):
                    setting, stype = setting
                else:
                    stype = None

                value = csettings.get(setting)

                if value is None:
                    self._if.ResetParameterValue([component, setting])
                    continue

                if stype:
                    value = stype(value)
                elif isinstance(value, bool):
                    value = dbus.Boolean(value)
                elif isinstance(value, int):
                    value = dbus.UInt32(value)
                elif isinstance(value, str):
                    value = dbus.String(value)
                else:
                    log.warning("Unknown type %s for setting '%s' = %r.",
                                type(value), setting, value)

                if component == 'engine':
                    self.set_engine_parameter(setting, value)
                elif component == 'driver':
                    self.set_driver_parameter(setting, value)
<commit_msg>Add method to add handler for signal from JackControl dbus interface<commit_after># -*- coding: utf-8 -*-
"""Control and configure a JACK server via DBus."""

import logging

from functools import partial

import dbus

log = logging.getLogger(__name__)
SETTINGS = {
    'engine': (
        'driver',
        'realtime',
        ('realtime-priority', dbus.Int32),
        'port-max',
        'verbose',
        ('client-timeout', dbus.Int32),
    ),
    'driver': (
        'capture',
        'playback',
        'device',
        'rate',
        'period',
        'nperiods',
        'outchannels',
        'inchannels',
        ('channels', dbus.Int32),
        'midi',
        'hwmon',
        'hwmeter',
        'shorts',
        'softmode',
    )
}

def get_jack_controller(bus=None):
    if not bus:
        bus = dbus.SessionBus()
    return bus.get_object("org.jackaudio.service", "/org/jackaudio/Controller")

class JackBaseInterface:
    def __init__(self, jackctl=None):
        if not jackctl:
            jackctl = get_jack_controller()

        self._if = dbus.Interface(jackctl, self.interface)

    def _async_handler(self, *args, **kw):
        name = kw.get('name')
        callback = kw.get('callback')

        if args and isinstance(args[0], dbus.DBusException):
            log.error("Async call failed name=%s: %s", name, args[0])
            return

        if callback:
            callback(*args, name=name)

    def call_async(self, meth, name, callback=None, *args):
        if callback:
            handler = partial(self._async_handler, callback=callback, name=name)
            kw = dict(reply_handler=handler, error_handler=handler)
        else:
            kw = {}
        return getattr(self._if, meth)(*args, **kw)

class JackCtlInterface(JackBaseInterface):
    interface = "org.jackaudio.JackControl"

    def is_started(self, cb=None):
        return self.call_async('IsStarted', 'is_started', cb)

    def is_realtime(self, cb=None):
        return self.call_async('IsRealtime', 'is_realtime', cb)

    def start_server(self, cb=None):
        return self.call_async('StartServer', 'start_server', cb)

    def stop_server(self, cb=None):
        return self.call_async('StopServer', 'stop_server', cb)

    def get_latency(self, cb=None):
        return self.call_async('GetLatency', 'latency', cb)

    def get_load(self, cb=None):
        return self.call_async('GetLoad', 'load', cb)

    def get_period(self, cb=None):
        return self.call_async('GetBufferSize', 'period', cb)

    def get_sample_rate(self, cb=None):
        return self.call_async('GetSampleRate', 'samplerate', cb)

    def get_xruns(self, cb=None):
        return self.call_async('GetXruns', 'xruns', cb)

    def add_signal_handler(self, handler, signal=None):
        return self._if.connect_to_signal(
            signal_name=signal,
            handler_function=handler,
            interface_keyword='interface',
            member_keyword='signal')

class JackCfgInterface(JackBaseInterface):
    interface = "org.jackaudio.Configure"

    def engine_has_feature(self, feature):
        try:
            features = self._if.ReadContainer(["engine"])[1]
        except:
            features = ()
        return dbus.String(feature) in features

    def get_engine_parameter(self, parameter, fallback=None):
        if not self.engine_has_feature(parameter):
            return fallback
        else:
            try:
                return self._if.GetParameterValue(["engine", parameter])[2]
            except:
                return fallback

    def set_engine_parameter(self, parameter, value, optional=True):
        if not self.engine_has_feature(parameter):
            return False
        elif optional:
            pvalue = self._if.GetParameterValue(["engine", parameter])

            if pvalue is None:
                return False

            if value != pvalue[2]:
                return bool(self._if.SetParameterValue(["engine", parameter],
                                                       value))
            else:
                return False
        else:
            return bool(self._if.SetParameterValue(["engine", parameter],
                                                   value))

    def driver_has_feature(self, feature):
        try:
            features = self._if.ReadContainer(["driver"])[1]
        except:
            features = ()
        return dbus.String(feature) in features

    def get_driver_parameter(self, parameter, fallback=None):
        if not self.driver_has_feature(parameter):
            return fallback
        else:
            try:
                return self._if.GetParameterValue(["driver", parameter])[2]
            except:
                return fallback

    def set_driver_parameter(self, parameter, value, optional=True):
        if not self.driver_has_feature(parameter):
            return False
        elif optional:
            if value != self._if.GetParameterValue(["driver", parameter])[2]:
                return bool(self._if.SetParameterValue(["driver", parameter],
                                                       value))
            else:
                return False
        else:
            return bool(self._if.SetParameterValue(["driver", parameter],
                                                   value))

    def activate_preset(self, settings):
        for component in ('engine', 'driver'):
            csettings = settings.get(component, {})

            for setting in SETTINGS[component]:
                if isinstance(setting, tuple):
                    setting, stype = setting
                else:
                    stype = None

                value = csettings.get(setting)

                if value is None:
                    self._if.ResetParameterValue([component, setting])
                    continue

                if stype:
                    value = stype(value)
                elif isinstance(value, bool):
                    value = dbus.Boolean(value)
                elif isinstance(value, int):
                    value = dbus.UInt32(value)
                elif isinstance(value, str):
                    value = dbus.String(value)
                else:
                    log.warning("Unknown type %s for setting '%s' = %r.",
                                type(value), setting, value)

                if component == 'engine':
                    self.set_engine_parameter(setting, value)
                elif component == 'driver':
                    self.set_driver_parameter(setting, value)
<|endoftext|>


<commit_before>import json
import re

from brazilnum.cnpj import format_cnpj
from brazilnum.cpf import format_cpf
from django.contrib.admin import SimpleListFilter
from django.forms.widgets import Widget
from simple_history.admin import SimpleHistoryAdmin

from jarbas.core.models import Reimbursement
from jarbas.dashboard.sites import dashboard

ALL_FIELDS = sorted(Reimbursement._meta.fields, key=lambda f: f.verbose_name)
CUSTOM_WIDGETS = ('receipt_url', 'subquota_description', 'suspicions')
READONLY_FIELDS = (f.name for f in ALL_FIELDS if f.name not in CUSTOM_WIDGETS)

class ReceiptUrlWidget(Widget):

    def render(self, name, value, attrs=None, renderer=None):
        if not value:
            return ''

        url = '
{}
'
        return url.format(value, value)

class SuspiciousWidget(Widget):

    SUSPICIONS = (
        'meal_price_outlier',
        'over_monthly_subquota_limit',
        'suspicious_traveled_speed_day',
        'invalid_cnpj_cpf',
        'election_expenses',
        'irregular_companies_classifier'
    )

    HUMAN_NAMES = (
        'Preço de refeição muito incomum',
        'Extrapolou limita da (sub)quota',
        'Muitas despesas em diferentes cidades no mesmo dia',
        'CPF ou CNPJ inválidos',
        'Gasto com campanha eleitoral',
        'CNPJ irregular'
    )

    MAP = dict(zip(SUSPICIONS, HUMAN_NAMES))

    def render(self, name, value, attrs=None, renderer=None):
        value_as_dict = json.loads(value)
        if not value_as_dict:
            return ''

        values = (self.MAP.get(k, k) for k in value_as_dict.keys())
        suspicions = '
'.join(values)
        return '
{}
'.format(suspicions)

class SuspiciousListFilter(SimpleListFilter):

    title = 'reembolso suspeito'
    parameter_name = 'is_suspicions'
    options = (
        ('yes', 'Sim'),
        ('no', 'Não'),
    )

    def lookups(self, request, model_admin):
        return self.options

    def queryset(self, request, queryset):
        return queryset.suspicions() if self.value() == 'yes' else queryset

class Subquotas:

    EN_US = (
        'Maintenance of office supporting parliamentary activity',
        'Locomotion, meal and lodging',
        'Fuels and lubricants',
        'Consultancy, research and technical work',
        'Publicity of parliamentary activity',
        'Purchase of office supplies',
        'Software purchase or renting; Postal services; Subscriptions',
        'Security service provided by specialized company',
        'Flight tickets',
        'Telecommunication',
        'Postal services',
        'Publication subscriptions',
        'Congressperson meal',
        'Lodging, except for congressperson from Distrito Federal',
        'Automotive vehicle renting or watercraft charter',
        'Aircraft renting or charter of aircraft',
        'Automotive vehicle renting or charter',
        'Watercraft renting or charter',
        'Taxi, toll and parking',
        'Terrestrial, maritime and fluvial tickets',
        'Participation in course, talk or similar event',
        'Flight ticket issue'
    )

    PT_BR = (
        'Manutenção de escritório de apoio à atividade parlamentar',
        'Locomoção, alimentação e  hospedagem',
        'Combustíveis e lubrificantes',
        'Consultorias, pesquisas e trabalhos técnicos',
        'Divulgação da atividade parlamentar',
        'Aquisição de material de escritório',
        'Aquisição ou loc. de software serv. postais ass.',
        'Serviço de segurança prestado por empresa especializada',
        'Passagens aéreas',
        'Telefonia',
        'Serviços postais',
        'Assinatura de publicações',
        'Fornecimento de alimentação do parlamentar',
        'Hospedagem ,exceto do parlamentar no distrito federal',
        'Locação de veículos automotores ou fretamento de embarcações',
        'Locação ou fretamento de aeronaves',
        'Locação ou fretamento de veículos automotores',
        'Locação ou fretamento de embarcações',
        'Serviço de táxi, pedágio e estacionamento',
        'Passagens terrestres, marítimas ou fluviais',
        'Participação em curso, palestra ou evento similar',
        'Emissão bilhete aéreo'
    )

    NUMBERS = (
        1,
        2,
        3,
        4,
        5,
        6,
        7,
        8,
        9,
        10,
        11,
        12,
        13,
        14,
        15,
        119,
        120,
        121,
        122,
        123,
        137,
        999
    )

    OPTIONS = sorted(zip(NUMBERS, PT_BR), key=lambda t: t[1])

    @classmethod
    def pt_br(cls, en_us):
        translations = dict(zip(cls.EN_US, cls.PT_BR))
        return translations.get(en_us)

    def en_us(cls, pt_br):
        translations = dict(zip(cls.PT_BR, cls.EN_US))
        return translations.get(pt_br)

class SubquotaWidget(Widget, Subquotas):

    def render(self, name, value, attrs=None, renderer=None):
        value = self.pt_br(value) or value
        return '
{}
'.format(value)

class SubquotaListFilter(SimpleListFilter, Subquotas):

    title = 'subcota'
    parameter_name = 'subquota_id'

    def lookups(self, request, model_admin):
        return self.OPTIONS

    def queryset(self, request, queryset):
        if not self.value():
            return queryset
        return queryset.filter(subquota_id=self.value())

class ReimbursementModelAdmin(SimpleHistoryAdmin):

    list_display = (
        'short_document_id',
        'jarbas',
        'congressperson_name',
        'year',
        'subquota_translated',
        'supplier_info',
        'value',
        'suspicious',
        # 'still_available',
    )

    search_fields = (
        'applicant_id',
        'cnpj_cpf',
        'congressperson_name',
        'document_id',
        'party',
        'state',
        'supplier',
        'subquota_description',
    )

    list_filter = (
        SuspiciousListFilter,
        # 'available_in_latest_dataset',
        'state',
        'year',
        SubquotaListFilter,
    )

    fields = tuple(f.name for f in ALL_FIELDS)
    readonly_fields = tuple(READONLY_FIELDS)

    def _format_document(self, obj):
        if obj.cnpj_cpf:
            if len(obj.cnpj_cpf) == 14:
                return format_cnpj(obj.cnpj_cpf)

            if len(obj.cnpj_cpf) == 11:
                return format_cpf(obj.cnpj_cpf)

            return obj.cnpj_cpf

    def supplier_info(self, obj):
        return '{}
{}'.format(obj.supplier, self._format_document(obj))

    supplier_info.short_description = 'Fornecedor'
    supplier_info.allow_tags = True

    def jarbas(self, obj):
        base_url = 'https://jarbas.serenatadeamor.org/#/documentId/{}/'
        url = base_url.format(obj.document_id)
        image_src = '/static/favicon/favicon-16x16.png'
        image = ''.format(image_src)
        return '{}'.format(url, image)

    jarbas.short_description = ''
    jarbas.allow_tags = True

    def suspicious(self, obj):
        return obj.suspicions is not None

    suspicious.short_description = 'suspeito'
    suspicious.boolean = True

    def value(self, obj):
        return 'R$ {:.2f}'.format(obj.total_net_value).replace('.', ',')

    value.short_description = 'valor'
    value.admin_order_field = 'total_net_value'

    def still_available(self, obj):
        return obj.available_in_latest_dataset

    still_available.short_description = 'disponível na Câmara'
    still_available.boolean = True

    def short_document_id(self, obj):
        return obj.document_id

    short_document_id.short_description = 'Reembolso'

    def subquota_translated(self, obj):
        return Subquotas.pt_br(obj.subquota_description)

    def has_add_permission(self, request):
        return False

    def has_change_permission(self, request, obj=None):
        if request.method != 'GET':
            return False
        return True

    def has_delete_permission(self, request, obj=None):
        return False

    @staticmethod
    def rename_change_url(url):
        if 'change' in url.regex.pattern:
            new_re = url.regex.pattern.replace('change', 'details')
            url.regex = re.compile(new_re, re.UNICODE)
        return url

    def get_urls(self):
        urls = filter(dashboard.valid_url, super().get_urls())
        return list(map(self.rename_change_url, urls))

    def get_object(self, request, object_id, from_field=None):
        obj = super().get_object(request, object_id, from_field)
        if obj and not obj.receipt_fetched:
            obj.get_receipt_url()
        return obj

    def formfield_for_dbfield(self, db_field, **kwargs):
        if db_field.name in CUSTOM_WIDGETS:
            widgets = dict(
                subquota_description=SubquotaWidget,
                receipt_url=ReceiptUrlWidget,
                suspicions=SuspiciousWidget
            )
            kwargs['widget'] = widgets.get(db_field.name)
        return super().formfield_for_dbfield(db_field, **kwargs)

dashboard.register(Reimbursement, ReimbursementModelAdmin)
<commit_msg>Add subquota as a list filter<commit_after>import json
import re

from brazilnum.cnpj import format_cnpj
from brazilnum.cpf import format_cpf
from django.contrib.admin import SimpleListFilter
from django.forms.widgets import Widget
from simple_history.admin import SimpleHistoryAdmin

from jarbas.core.models import Reimbursement
from jarbas.dashboard.sites import dashboard

ALL_FIELDS = sorted(Reimbursement._meta.fields, key=lambda f: f.verbose_name)
CUSTOM_WIDGETS = ('receipt_url', 'subquota_description', 'suspicions')
READONLY_FIELDS = (f.name for f in ALL_FIELDS if f.name not in CUSTOM_WIDGETS)

class ReceiptUrlWidget(Widget):

    def render(self, name, value, attrs=None, renderer=None):
        if not value:
            return ''

        url = '
{}
'
        return url.format(value, value)

class SuspiciousWidget(Widget):

    SUSPICIONS = (
        'meal_price_outlier',
        'over_monthly_subquota_limit',
        'suspicious_traveled_speed_day',
        'invalid_cnpj_cpf',
        'election_expenses',
        'irregular_companies_classifier'
    )

    HUMAN_NAMES = (
        'Preço de refeição muito incomum',
        'Extrapolou limita da (sub)quota',
        'Muitas despesas em diferentes cidades no mesmo dia',
        'CPF ou CNPJ inválidos',
        'Gasto com campanha eleitoral',
        'CNPJ irregular'
    )

    MAP = dict(zip(SUSPICIONS, HUMAN_NAMES))

    def render(self, name, value, attrs=None, renderer=None):
        value_as_dict = json.loads(value)
        if not value_as_dict:
            return ''

        values = (self.MAP.get(k, k) for k in value_as_dict.keys())
        suspicions = '
'.join(values)
        return '
{}
'.format(suspicions)

class SuspiciousListFilter(SimpleListFilter):

    title = 'reembolso suspeito'
    parameter_name = 'is_suspicions'
    options = (
        ('yes', 'Sim'),
        ('no', 'Não'),
    )

    def lookups(self, request, model_admin):
        return self.options

    def queryset(self, request, queryset):
        return queryset.suspicions() if self.value() == 'yes' else queryset

class Subquotas:

    EN_US = (
        'Maintenance of office supporting parliamentary activity',
        'Locomotion, meal and lodging',
        'Fuels and lubricants',
        'Consultancy, research and technical work',
        'Publicity of parliamentary activity',
        'Purchase of office supplies',
        'Software purchase or renting; Postal services; Subscriptions',
        'Security service provided by specialized company',
        'Flight tickets',
        'Telecommunication',
        'Postal services',
        'Publication subscriptions',
        'Congressperson meal',
        'Lodging, except for congressperson from Distrito Federal',
        'Automotive vehicle renting or watercraft charter',
        'Aircraft renting or charter of aircraft',
        'Automotive vehicle renting or charter',
        'Watercraft renting or charter',
        'Taxi, toll and parking',
        'Terrestrial, maritime and fluvial tickets',
        'Participation in course, talk or similar event',
        'Flight ticket issue'
    )

    PT_BR = (
        'Manutenção de escritório de apoio à atividade parlamentar',
        'Locomoção, alimentação e  hospedagem',
        'Combustíveis e lubrificantes',
        'Consultorias, pesquisas e trabalhos técnicos',
        'Divulgação da atividade parlamentar',
        'Aquisição de material de escritório',
        'Aquisição ou loc. de software serv. postais ass.',
        'Serviço de segurança prestado por empresa especializada',
        'Passagens aéreas',
        'Telefonia',
        'Serviços postais',
        'Assinatura de publicações',
        'Fornecimento de alimentação do parlamentar',
        'Hospedagem ,exceto do parlamentar no distrito federal',
        'Locação de veículos automotores ou fretamento de embarcações',
        'Locação ou fretamento de aeronaves',
        'Locação ou fretamento de veículos automotores',
        'Locação ou fretamento de embarcações',
        'Serviço de táxi, pedágio e estacionamento',
        'Passagens terrestres, marítimas ou fluviais',
        'Participação em curso, palestra ou evento similar',
        'Emissão bilhete aéreo'
    )

    NUMBERS = (
        1,
        2,
        3,
        4,
        5,
        6,
        7,
        8,
        9,
        10,
        11,
        12,
        13,
        14,
        15,
        119,
        120,
        121,
        122,
        123,
        137,
        999
    )

    OPTIONS = sorted(zip(NUMBERS, PT_BR), key=lambda t: t[1])

    @classmethod
    def pt_br(cls, en_us):
        translations = dict(zip(cls.EN_US, cls.PT_BR))
        return translations.get(en_us)

    def en_us(cls, pt_br):
        translations = dict(zip(cls.PT_BR, cls.EN_US))
        return translations.get(pt_br)

class SubquotaWidget(Widget, Subquotas):

    def render(self, name, value, attrs=None, renderer=None):
        value = self.pt_br(value) or value
        return '
{}
'.format(value)

class SubquotaListFilter(SimpleListFilter, Subquotas):

    title = 'subcota'
    parameter_name = 'subquota_id'
    default_value = None

    def lookups(self, request, model_admin):
        return self.OPTIONS

    def value(self):
        try:
            return int(super().value())
        except ValueError:
            return None

    def queryset(self, request, queryset):
        subquota = dict(self.OPTIONS).get(self.value())
        if not subquota:
            return queryset
        return queryset.filter(subquota_description=self.en_us(subquota))

class ReimbursementModelAdmin(SimpleHistoryAdmin):

    list_display = (
        'short_document_id',
        'jarbas',
        'congressperson_name',
        'year',
        'subquota_translated',
        'supplier_info',
        'value',
        'suspicious',
        # 'still_available',
    )

    search_fields = (
        'applicant_id',
        'cnpj_cpf',
        'congressperson_name',
        'document_id',
        'party',
        'state',
        'supplier',
        'subquota_description',
    )

    list_filter = (
        SuspiciousListFilter,
        # 'available_in_latest_dataset',
        'state',
        'year',
        SubquotaListFilter,
    )

    fields = tuple(f.name for f in ALL_FIELDS)
    readonly_fields = tuple(READONLY_FIELDS)

    def _format_document(self, obj):
        if obj.cnpj_cpf:
            if len(obj.cnpj_cpf) == 14:
                return format_cnpj(obj.cnpj_cpf)

            if len(obj.cnpj_cpf) == 11:
                return format_cpf(obj.cnpj_cpf)

            return obj.cnpj_cpf

    def supplier_info(self, obj):
        return '{}
{}'.format(obj.supplier, self._format_document(obj))

    supplier_info.short_description = 'Fornecedor'
    supplier_info.allow_tags = True

    def jarbas(self, obj):
        base_url = 'https://jarbas.serenatadeamor.org/#/documentId/{}/'
        url = base_url.format(obj.document_id)
        image_src = '/static/favicon/favicon-16x16.png'
        image = ''.format(image_src)
        return '{}'.format(url, image)

    jarbas.short_description = ''
    jarbas.allow_tags = True

    def suspicious(self, obj):
        return obj.suspicions is not None

    suspicious.short_description = 'suspeito'
    suspicious.boolean = True

    def value(self, obj):
        return 'R$ {:.2f}'.format(obj.total_net_value).replace('.', ',')

    value.short_description = 'valor'
    value.admin_order_field = 'total_net_value'

    def still_available(self, obj):
        return obj.available_in_latest_dataset

    still_available.short_description = 'disponível na Câmara'
    still_available.boolean = True

    def short_document_id(self, obj):
        return obj.document_id

    short_document_id.short_description = 'Reembolso'

    def subquota_translated(self, obj):
        return Subquotas.pt_br(obj.subquota_description)

    def has_add_permission(self, request):
        return False

    def has_change_permission(self, request, obj=None):
        if request.method != 'GET':
            return False
        return True

    def has_delete_permission(self, request, obj=None):
        return False

    @staticmethod
    def rename_change_url(url):
        if 'change' in url.regex.pattern:
            new_re = url.regex.pattern.replace('change', 'details')
            url.regex = re.compile(new_re, re.UNICODE)
        return url

    def get_urls(self):
        urls = filter(dashboard.valid_url, super().get_urls())
        return list(map(self.rename_change_url, urls))

    def get_object(self, request, object_id, from_field=None):
        obj = super().get_object(request, object_id, from_field)
        if obj and not obj.receipt_fetched:
            obj.get_receipt_url()
        return obj

    def formfield_for_dbfield(self, db_field, **kwargs):
        if db_field.name in CUSTOM_WIDGETS:
            widgets = dict(
                subquota_description=SubquotaWidget,
                receipt_url=ReceiptUrlWidget,
                suspicions=SuspiciousWidget
            )
            kwargs['widget'] = widgets.get(db_field.name)
        return super().formfield_for_dbfield(db_field, **kwargs)

dashboard.register(Reimbursement, ReimbursementModelAdmin)
<|endoftext|>