<commit_before>import mock
import pytest

from rest_framework.exceptions import PermissionDenied

from awx.api.versioning import reverse
from awx.api.serializers import JobTemplateSerializer
from awx.main.access import (
    BaseAccess,
    JobTemplateAccess,
    ScheduleAccess
)
from awx.main.models.jobs import JobTemplate
from awx.main.models.organization import Organization
from awx.main.models.schedules import Schedule

@pytest.fixture
def jt_linked(job_template_factory, credential, net_credential, vault_credential):
    '''
    A job template with a reasonably complete set of related objects to
    test RBAC and other functionality affected by related objects
    '''
    objects = job_template_factory(
        'testJT', organization='org1', project='proj1', inventory='inventory1',
        credential='cred1')
    jt = objects.job_template
    jt.credentials.add(vault_credential)
    jt.save()
    # Add AWS cloud credential and network credential
    jt.credentials.add(credential)
    jt.credentials.add(net_credential)
    return jt

@mock.patch.object(BaseAccess, 'check_license', return_value=None)
@pytest.mark.django_db
def test_job_template_access_superuser(check_license, user, deploy_jobtemplate):
    # GIVEN a superuser
    u = user('admin', True)
    # WHEN access to a job template is checked
    access = JobTemplateAccess(u)
    # THEN all access checks should pass
    assert access.can_read(deploy_jobtemplate)
    assert access.can_add({})

@pytest.mark.django_db
def test_job_template_access_read_level(jt_linked, rando):

    access = JobTemplateAccess(rando)
    jt_linked.project.read_role.members.add(rando)
    jt_linked.inventory.read_role.members.add(rando)
    jt_linked.get_deprecated_credential('ssh').read_role.members.add(rando)

    proj_pk = jt_linked.project.pk
    assert not access.can_add(dict(inventory=jt_linked.inventory.pk, project=proj_pk))
    assert not access.can_add(dict(credential=jt_linked.credential, project=proj_pk))
    assert not access.can_add(dict(vault_credential=jt_linked.vault_credential, project=proj_pk))

    for cred in jt_linked.credentials.all():
        assert not access.can_unattach(jt_linked, cred, 'credentials', {})

@pytest.mark.django_db
def test_job_template_access_use_level(jt_linked, rando):

    access = JobTemplateAccess(rando)
    jt_linked.project.use_role.members.add(rando)
    jt_linked.inventory.use_role.members.add(rando)
    jt_linked.get_deprecated_credential('ssh').use_role.members.add(rando)
    jt_linked.get_deprecated_credential('vault').use_role.members.add(rando)

    proj_pk = jt_linked.project.pk
    assert access.can_add(dict(inventory=jt_linked.inventory.pk, project=proj_pk))
    assert access.can_add(dict(credential=jt_linked.credential, project=proj_pk))
    assert access.can_add(dict(vault_credential=jt_linked.vault_credential, project=proj_pk))

    for cred in jt_linked.credentials.all():
        assert not access.can_unattach(jt_linked, cred, 'credentials', {})

@pytest.mark.django_db
@pytest.mark.parametrize("role_names", [("admin_role",), ("inventory_admin_role", "project_admin_role")])
def test_job_template_access_admin(role_names, jt_linked, rando):
    access = JobTemplateAccess(rando)
    # Appoint this user as admin of the organization
    #jt_linked.inventory.organization.admin_role.members.add(rando)
    for role_name in role_names:
        role = getattr(jt_linked.inventory.organization, role_name)
        role.members.add(rando)

    # Assign organization permission in the same way the create view does
    organization = jt_linked.inventory.organization
    jt_linked.get_deprecated_credential('ssh').admin_role.parents.add(organization.admin_role)

    proj_pk = jt_linked.project.pk
    assert access.can_add(dict(inventory=jt_linked.inventory.pk, project=proj_pk))
    assert access.can_add(dict(credential=jt_linked.credential, project=proj_pk))

    for cred in jt_linked.credentials.all():
        assert access.can_unattach(jt_linked, cred, 'credentials', {})

    assert access.can_read(jt_linked)
    assert access.can_delete(jt_linked)

@pytest.mark.django_db
def test_job_template_extra_credentials_prompts_access(
        rando, post, inventory, project, machine_credential, vault_credential):
    jt = JobTemplate.objects.create(
        name = 'test-jt',
        project = project,
        playbook = 'helloworld.yml',
        inventory = inventory,
        ask_credential_on_launch = True
    )
    jt.credentials.add(machine_credential)
    jt.execute_role.members.add(rando)
    r = post(
        reverse('api:job_template_launch', kwargs={'version': 'v2', 'pk': jt.id}),
        {'vault_credential': vault_credential.pk}, rando
    )
    assert r.status_code == 403

@pytest.mark.django_db
class TestJobTemplateCredentials:

    def test_job_template_cannot_add_extra_credentials(self, job_template, credential, rando):
        job_template.admin_role.members.add(rando)
        credential.read_role.members.add(rando)
        # without permission to credential, user can not attach it
        assert not JobTemplateAccess(rando).can_attach(
            job_template, credential, 'credentials', {})

    def test_job_template_can_add_extra_credentials(self, job_template, credential, rando):
        job_template.admin_role.members.add(rando)
        credential.use_role.members.add(rando)
        # user has permission to apply credential
        assert JobTemplateAccess(rando).can_attach(
            job_template, credential, 'credentials', {})

    def test_job_template_vault_cred_check(self, mocker, job_template, vault_credential, rando, project):
        # TODO: remove in 3.4
        job_template.admin_role.members.add(rando)
        # not allowed to use the vault cred
        # this is checked in the serializer validate method, not access.py
        view = mocker.MagicMock()
        view.request = mocker.MagicMock()
        view.request.user = rando
        serializer = JobTemplateSerializer(job_template, context={'view': view})
        with pytest.raises(PermissionDenied):
            serializer.validate({
                'vault_credential': vault_credential.pk,
                'project': project,  # necessary because job_template fixture fails validation
                'ask_inventory_on_launch': True,
            })

    def test_job_template_vault_cred_check_noop(self, mocker, job_template, vault_credential, rando, project):
        # TODO: remove in 3.4
        job_template.credentials.add(vault_credential)
        job_template.admin_role.members.add(rando)
        # not allowed to use the vault cred
        # this is checked in the serializer validate method, not access.py
        view = mocker.MagicMock()
        view.request = mocker.MagicMock()
        view.request.user = rando
        serializer = JobTemplateSerializer(job_template, context={'view': view})
        # should not raise error:
        serializer.validate({
            'vault_credential': vault_credential.pk,
            'project': project,  # necessary because job_template fixture fails validation
            'playbook': 'helloworld.yml',
            'ask_inventory_on_launch': True,
        })

    def test_new_jt_with_vault(self, mocker, vault_credential, project, rando):
        project.admin_role.members.add(rando)
        # TODO: remove in 3.4
        # this is checked in the serializer validate method, not access.py
        view = mocker.MagicMock()
        view.request = mocker.MagicMock()
        view.request.user = rando
        serializer = JobTemplateSerializer(context={'view': view})
        with pytest.raises(PermissionDenied):
            serializer.validate({
                'vault_credential': vault_credential.pk,
                'project': project,
                'playbook': 'helloworld.yml',
                'ask_inventory_on_launch': True,
                'name': 'asdf'
            })

@pytest.mark.django_db
class TestOrphanJobTemplate:

    def test_orphan_JT_readable_by_system_auditor(self, job_template, system_auditor):
        assert system_auditor.is_system_auditor
        assert job_template.project is None
        access = JobTemplateAccess(system_auditor)
        assert access.can_read(job_template)

    def test_system_admin_orphan_capabilities(self, job_template, admin_user):
        job_template.capabilities_cache = {'edit': False}
        access = JobTemplateAccess(admin_user)
        capabilities = access.get_user_capabilities(job_template, method_list=['edit'])
        assert capabilities['edit']

@pytest.mark.django_db
@pytest.mark.job_permissions
def test_job_template_creator_access(project, rando, post):

    project.admin_role.members.add(rando)
    with mock.patch(
            'awx.main.models.projects.ProjectOptions.playbooks',
            new_callable=mock.PropertyMock(return_value=['helloworld.yml'])):
        response = post(reverse('api:job_template_list'), dict(
            name='newly-created-jt',
            job_type='run',
            ask_inventory_on_launch=True,
            ask_credential_on_launch=True,
            project=project.pk,
            playbook='helloworld.yml'
        ), rando)

    assert response.status_code == 201
    jt_pk = response.data['id']
    jt_obj = JobTemplate.objects.get(pk=jt_pk)
    # Creating a JT should place the creator in the admin role
    assert rando in jt_obj.admin_role

@pytest.mark.django_db
def test_associate_label(label, user, job_template):
    access = JobTemplateAccess(user('joe', False))
    job_template.admin_role.members.add(user('joe', False))
    label.organization.read_role.members.add(user('joe', False))
    assert access.can_attach(job_template, label, 'labels', None)

@pytest.mark.django_db
class TestJobTemplateSchedules:

    rrule = 'DTSTART:20151117T050000Z RRULE:FREQ=DAILY;INTERVAL=1;COUNT=1'
    rrule2 = 'DTSTART:20151117T050000Z RRULE:FREQ=WEEKLY;INTERVAL=1;COUNT=1'

    @pytest.fixture
    def jt2(self):
        return JobTemplate.objects.create(name="other-jt")

    def test_move_schedule_to_JT_no_access(self, job_template, rando, jt2):
        schedule = Schedule.objects.create(unified_job_template=job_template, rrule=self.rrule)
        job_template.admin_role.members.add(rando)
        access = ScheduleAccess(rando)
        assert not access.can_change(schedule, data=dict(unified_job_template=jt2.pk))

    def test_move_schedule_from_JT_no_access(self, job_template, rando, jt2):
        schedule = Schedule.objects.create(unified_job_template=job_template, rrule=self.rrule)
        jt2.admin_role.members.add(rando)
        access = ScheduleAccess(rando)
        assert not access.can_change(schedule, data=dict(unified_job_template=jt2.pk))

    def test_can_create_schedule_with_execute(self, job_template, rando):
        job_template.execute_role.members.add(rando)
        access = ScheduleAccess(rando)
        assert access.can_add({'unified_job_template': job_template})

    def test_can_modify_ones_own_schedule(self, job_template, rando):
        job_template.execute_role.members.add(rando)
        schedule = Schedule.objects.create(unified_job_template=job_template, rrule=self.rrule, created_by=rando)
        access = ScheduleAccess(rando)
        assert access.can_change(schedule, {'rrule': self.rrule2})

    def test_prompts_access_checked(self, job_template, inventory, credential, rando):
        job_template.execute_role.members.add(rando)
        access = ScheduleAccess(rando)
        data = dict(
            unified_job_template=job_template,
            rrule=self.rrule,
            created_by=rando,
            inventory=inventory,
            credentials=[credential]
        )
        with mock.patch('awx.main.access.JobLaunchConfigAccess.can_add') as mock_add:
            mock_add.return_value = True
            assert access.can_add(data)
            mock_add.assert_called_once_with(data)
        data.pop('credentials')
        schedule = Schedule.objects.create(**data)
        with mock.patch('awx.main.access.JobLaunchConfigAccess.can_change') as mock_change:
            mock_change.return_value = True
            assert access.can_change(schedule, {'inventory': 42})
            mock_change.assert_called_once_with(schedule, {'inventory': 42})

@pytest.mark.django_db
def test_jt_org_ownership_change(user, jt_linked):
    admin1 = user('admin1')
    org1 = jt_linked.project.organization
    org1.admin_role.members.add(admin1)
    a1_access = JobTemplateAccess(admin1)

    assert a1_access.can_read(jt_linked)

    admin2 = user('admin2')
    org2 = Organization.objects.create(name='mrroboto', description='domo')
    org2.admin_role.members.add(admin2)
    a2_access = JobTemplateAccess(admin2)

    assert not a2_access.can_read(jt_linked)

    jt_linked.project.organization = org2
    jt_linked.project.save()
    jt_linked.inventory.organization = org2
    jt_linked.inventory.save()

    assert a2_access.can_read(jt_linked)
    assert not a1_access.can_read(jt_linked)
<commit_msg>Update test to reflect needing job_template_admin_role<commit_after>import mock
import pytest

from rest_framework.exceptions import PermissionDenied

from awx.api.versioning import reverse
from awx.api.serializers import JobTemplateSerializer
from awx.main.access import (
    BaseAccess,
    JobTemplateAccess,
    ScheduleAccess
)
from awx.main.models.jobs import JobTemplate
from awx.main.models.organization import Organization
from awx.main.models.schedules import Schedule

@pytest.fixture
def jt_linked(job_template_factory, credential, net_credential, vault_credential):
    '''
    A job template with a reasonably complete set of related objects to
    test RBAC and other functionality affected by related objects
    '''
    objects = job_template_factory(
        'testJT', organization='org1', project='proj1', inventory='inventory1',
        credential='cred1')
    jt = objects.job_template
    jt.credentials.add(vault_credential)
    jt.save()
    # Add AWS cloud credential and network credential
    jt.credentials.add(credential)
    jt.credentials.add(net_credential)
    return jt

@mock.patch.object(BaseAccess, 'check_license', return_value=None)
@pytest.mark.django_db
def test_job_template_access_superuser(check_license, user, deploy_jobtemplate):
    # GIVEN a superuser
    u = user('admin', True)
    # WHEN access to a job template is checked
    access = JobTemplateAccess(u)
    # THEN all access checks should pass
    assert access.can_read(deploy_jobtemplate)
    assert access.can_add({})

@pytest.mark.django_db
def test_job_template_access_read_level(jt_linked, rando):

    access = JobTemplateAccess(rando)
    jt_linked.project.read_role.members.add(rando)
    jt_linked.inventory.read_role.members.add(rando)
    jt_linked.get_deprecated_credential('ssh').read_role.members.add(rando)

    proj_pk = jt_linked.project.pk
    assert not access.can_add(dict(inventory=jt_linked.inventory.pk, project=proj_pk))
    assert not access.can_add(dict(credential=jt_linked.credential, project=proj_pk))
    assert not access.can_add(dict(vault_credential=jt_linked.vault_credential, project=proj_pk))

    for cred in jt_linked.credentials.all():
        assert not access.can_unattach(jt_linked, cred, 'credentials', {})

@pytest.mark.django_db
def test_job_template_access_use_level(jt_linked, rando):

    access = JobTemplateAccess(rando)
    jt_linked.project.use_role.members.add(rando)
    jt_linked.inventory.use_role.members.add(rando)
    jt_linked.get_deprecated_credential('ssh').use_role.members.add(rando)
    jt_linked.get_deprecated_credential('vault').use_role.members.add(rando)

    proj_pk = jt_linked.project.pk
    assert access.can_add(dict(inventory=jt_linked.inventory.pk, project=proj_pk))
    assert access.can_add(dict(credential=jt_linked.credential, project=proj_pk))
    assert access.can_add(dict(vault_credential=jt_linked.vault_credential, project=proj_pk))

    for cred in jt_linked.credentials.all():
        assert not access.can_unattach(jt_linked, cred, 'credentials', {})

@pytest.mark.django_db
@pytest.mark.parametrize("role_names", [("admin_role",), ("job_template_admin_role", "inventory_admin_role", "project_admin_role")])
def test_job_template_access_admin(role_names, jt_linked, rando):
    access = JobTemplateAccess(rando)
    # Appoint this user as admin of the organization
    #jt_linked.inventory.organization.admin_role.members.add(rando)
    assert not access.can_read(jt_linked)
    assert not access.can_delete(jt_linked)

    for role_name in role_names:
        role = getattr(jt_linked.inventory.organization, role_name)
        role.members.add(rando)

    # Assign organization permission in the same way the create view does
    organization = jt_linked.inventory.organization
    jt_linked.get_deprecated_credential('ssh').admin_role.parents.add(organization.admin_role)

    proj_pk = jt_linked.project.pk
    assert access.can_add(dict(inventory=jt_linked.inventory.pk, project=proj_pk))
    assert access.can_add(dict(credential=jt_linked.credential, project=proj_pk))

    for cred in jt_linked.credentials.all():
        assert access.can_unattach(jt_linked, cred, 'credentials', {})

    assert access.can_read(jt_linked)
    assert access.can_delete(jt_linked)

@pytest.mark.django_db
def test_job_template_extra_credentials_prompts_access(
        rando, post, inventory, project, machine_credential, vault_credential):
    jt = JobTemplate.objects.create(
        name = 'test-jt',
        project = project,
        playbook = 'helloworld.yml',
        inventory = inventory,
        ask_credential_on_launch = True
    )
    jt.credentials.add(machine_credential)
    jt.execute_role.members.add(rando)
    r = post(
        reverse('api:job_template_launch', kwargs={'version': 'v2', 'pk': jt.id}),
        {'vault_credential': vault_credential.pk}, rando
    )
    assert r.status_code == 403

@pytest.mark.django_db
class TestJobTemplateCredentials:

    def test_job_template_cannot_add_extra_credentials(self, job_template, credential, rando):
        job_template.admin_role.members.add(rando)
        credential.read_role.members.add(rando)
        # without permission to credential, user can not attach it
        assert not JobTemplateAccess(rando).can_attach(
            job_template, credential, 'credentials', {})

    def test_job_template_can_add_extra_credentials(self, job_template, credential, rando):
        job_template.admin_role.members.add(rando)
        credential.use_role.members.add(rando)
        # user has permission to apply credential
        assert JobTemplateAccess(rando).can_attach(
            job_template, credential, 'credentials', {})

    def test_job_template_vault_cred_check(self, mocker, job_template, vault_credential, rando, project):
        # TODO: remove in 3.4
        job_template.admin_role.members.add(rando)
        # not allowed to use the vault cred
        # this is checked in the serializer validate method, not access.py
        view = mocker.MagicMock()
        view.request = mocker.MagicMock()
        view.request.user = rando
        serializer = JobTemplateSerializer(job_template, context={'view': view})
        with pytest.raises(PermissionDenied):
            serializer.validate({
                'vault_credential': vault_credential.pk,
                'project': project,  # necessary because job_template fixture fails validation
                'ask_inventory_on_launch': True,
            })

    def test_job_template_vault_cred_check_noop(self, mocker, job_template, vault_credential, rando, project):
        # TODO: remove in 3.4
        job_template.credentials.add(vault_credential)
        job_template.admin_role.members.add(rando)
        # not allowed to use the vault cred
        # this is checked in the serializer validate method, not access.py
        view = mocker.MagicMock()
        view.request = mocker.MagicMock()
        view.request.user = rando
        serializer = JobTemplateSerializer(job_template, context={'view': view})
        # should not raise error:
        serializer.validate({
            'vault_credential': vault_credential.pk,
            'project': project,  # necessary because job_template fixture fails validation
            'playbook': 'helloworld.yml',
            'ask_inventory_on_launch': True,
        })

    def test_new_jt_with_vault(self, mocker, vault_credential, project, rando):
        project.admin_role.members.add(rando)
        # TODO: remove in 3.4
        # this is checked in the serializer validate method, not access.py
        view = mocker.MagicMock()
        view.request = mocker.MagicMock()
        view.request.user = rando
        serializer = JobTemplateSerializer(context={'view': view})
        with pytest.raises(PermissionDenied):
            serializer.validate({
                'vault_credential': vault_credential.pk,
                'project': project,
                'playbook': 'helloworld.yml',
                'ask_inventory_on_launch': True,
                'name': 'asdf'
            })

@pytest.mark.django_db
class TestOrphanJobTemplate:

    def test_orphan_JT_readable_by_system_auditor(self, job_template, system_auditor):
        assert system_auditor.is_system_auditor
        assert job_template.project is None
        access = JobTemplateAccess(system_auditor)
        assert access.can_read(job_template)

    def test_system_admin_orphan_capabilities(self, job_template, admin_user):
        job_template.capabilities_cache = {'edit': False}
        access = JobTemplateAccess(admin_user)
        capabilities = access.get_user_capabilities(job_template, method_list=['edit'])
        assert capabilities['edit']

@pytest.mark.django_db
@pytest.mark.job_permissions
def test_job_template_creator_access(project, rando, post):

    project.admin_role.members.add(rando)
    with mock.patch(
            'awx.main.models.projects.ProjectOptions.playbooks',
            new_callable=mock.PropertyMock(return_value=['helloworld.yml'])):
        response = post(reverse('api:job_template_list'), dict(
            name='newly-created-jt',
            job_type='run',
            ask_inventory_on_launch=True,
            ask_credential_on_launch=True,
            project=project.pk,
            playbook='helloworld.yml'
        ), rando)

    assert response.status_code == 201
    jt_pk = response.data['id']
    jt_obj = JobTemplate.objects.get(pk=jt_pk)
    # Creating a JT should place the creator in the admin role
    assert rando in jt_obj.admin_role

@pytest.mark.django_db
def test_associate_label(label, user, job_template):
    access = JobTemplateAccess(user('joe', False))
    job_template.admin_role.members.add(user('joe', False))
    label.organization.read_role.members.add(user('joe', False))
    assert access.can_attach(job_template, label, 'labels', None)

@pytest.mark.django_db
class TestJobTemplateSchedules:

    rrule = 'DTSTART:20151117T050000Z RRULE:FREQ=DAILY;INTERVAL=1;COUNT=1'
    rrule2 = 'DTSTART:20151117T050000Z RRULE:FREQ=WEEKLY;INTERVAL=1;COUNT=1'

    @pytest.fixture
    def jt2(self):
        return JobTemplate.objects.create(name="other-jt")

    def test_move_schedule_to_JT_no_access(self, job_template, rando, jt2):
        schedule = Schedule.objects.create(unified_job_template=job_template, rrule=self.rrule)
        job_template.admin_role.members.add(rando)
        access = ScheduleAccess(rando)
        assert not access.can_change(schedule, data=dict(unified_job_template=jt2.pk))

    def test_move_schedule_from_JT_no_access(self, job_template, rando, jt2):
        schedule = Schedule.objects.create(unified_job_template=job_template, rrule=self.rrule)
        jt2.admin_role.members.add(rando)
        access = ScheduleAccess(rando)
        assert not access.can_change(schedule, data=dict(unified_job_template=jt2.pk))

    def test_can_create_schedule_with_execute(self, job_template, rando):
        job_template.execute_role.members.add(rando)
        access = ScheduleAccess(rando)
        assert access.can_add({'unified_job_template': job_template})

    def test_can_modify_ones_own_schedule(self, job_template, rando):
        job_template.execute_role.members.add(rando)
        schedule = Schedule.objects.create(unified_job_template=job_template, rrule=self.rrule, created_by=rando)
        access = ScheduleAccess(rando)
        assert access.can_change(schedule, {'rrule': self.rrule2})

    def test_prompts_access_checked(self, job_template, inventory, credential, rando):
        job_template.execute_role.members.add(rando)
        access = ScheduleAccess(rando)
        data = dict(
            unified_job_template=job_template,
            rrule=self.rrule,
            created_by=rando,
            inventory=inventory,
            credentials=[credential]
        )
        with mock.patch('awx.main.access.JobLaunchConfigAccess.can_add') as mock_add:
            mock_add.return_value = True
            assert access.can_add(data)
            mock_add.assert_called_once_with(data)
        data.pop('credentials')
        schedule = Schedule.objects.create(**data)
        with mock.patch('awx.main.access.JobLaunchConfigAccess.can_change') as mock_change:
            mock_change.return_value = True
            assert access.can_change(schedule, {'inventory': 42})
            mock_change.assert_called_once_with(schedule, {'inventory': 42})

@pytest.mark.django_db
def test_jt_org_ownership_change(user, jt_linked):
    admin1 = user('admin1')
    org1 = jt_linked.project.organization
    org1.admin_role.members.add(admin1)
    a1_access = JobTemplateAccess(admin1)

    assert a1_access.can_read(jt_linked)

    admin2 = user('admin2')
    org2 = Organization.objects.create(name='mrroboto', description='domo')
    org2.admin_role.members.add(admin2)
    a2_access = JobTemplateAccess(admin2)

    assert not a2_access.can_read(jt_linked)

    jt_linked.project.organization = org2
    jt_linked.project.save()
    jt_linked.inventory.organization = org2
    jt_linked.inventory.save()

    assert a2_access.can_read(jt_linked)
    assert not a1_access.can_read(jt_linked)
<|endoftext|>


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

from tornado.web import HTTPError

class JsonRequestError(HTTPError):

    def __init__(self, error_message, *args, **kwargs):
        super(JsonRequestError, self).__init__(400, error_message)

class JsonValidateMessagesMixin(object):
    default_error_message = ''

    def validate_field_exist(self, key):
        value = self.request_json.get(key, None)
        if not value:
            raise JsonRequestError("%s is required" % key)

    def validate_fields_scope(self, json_keys, valide_keys):
        if set(json_keys) - set(valide_keys):
            raise JsonRequestError("Invalide key included")

class JsonRequestResponseMixin(JsonValidateMessagesMixin):

    """
    Extends JSONResponseMixin.  Attempts to parse request as JSON.  If request
    is properly formatted, the json is saved to self.request_json as a Python
    object.  request_json will be None for imparsible requests.
    Set the attribute require_json to True to return a 400 "Bad Request" error
    for requests that don't contain JSON.

    Note: To allow public access to your view, you'll need to use the
    csrf_exempt decorator or CsrfExemptMixin.

    """

    error_response_message = 'Improperly formatted request'
    json_required = {'GET': False, 'POST': True,
                     'PUT': True, 'DELETE': False}

    def json_validate(self):
        pass

    def render_bad_request_response(self, error_dict=None):
        if error_dict is None:
            error_dict = self.error_response_dict
        self.set_status(400)
        self.finish(error_dict)

    def get_request_json(self):
        try:
            return json.loads(self.request.body)
        except ValueError:
            raise JsonRequestError(error_message=self.error_response_message)

    def prepare(self):
        if self.json_required[self.request.method]:
            self.request_json = self.get_request_json()
            self.json_validate()
<commit_msg>[feature] Add method differ validate<commit_after># -*- coding: utf-8 -*-
import json

from tornado.web import HTTPError

class JsonRequestError(HTTPError):

    def __init__(self, error_message, *args, **kwargs):
        super(JsonRequestError, self).__init__(400, error_message)

class JsonValidateMessagesMixin(object):
    default_error_message = ''

    def validate_field_exist(self, key):
        value = self.request_json.get(key, None)
        if not value:
            raise JsonRequestError("%s is required" % key)

    def validate_fields_scope(self, json_keys, valide_keys):
        if set(json_keys) - set(valide_keys):
            raise JsonRequestError("Invalide key included")

    def validate_post(self):
        pass

    def validate_put(self):
        pass

    def json_validate(self, request_method):
        validate_method = getattr(self, request_method)
        validate_method()

class JsonRequestResponseMixin(JsonValidateMessagesMixin):

    """
    Extends JSONResponseMixin.  Attempts to parse request as JSON.  If request
    is properly formatted, the json is saved to self.request_json as a Python
    object.  request_json will be None for imparsible requests.
    Set the attribute require_json to True to return a 400 "Bad Request" error
    for requests that don't contain JSON.

    Note: To allow public access to your view, you'll need to use the
    csrf_exempt decorator or CsrfExemptMixin.

    """

    error_response_message = 'Improperly formatted request'
    json_required = {'GET': False, 'POST': True,
                     'PUT': True, 'DELETE': False}

    def json_validate(self):
        pass

    def render_bad_request_response(self, error_dict=None):
        if error_dict is None:
            error_dict = self.error_response_dict
        self.set_status(400)
        self.finish(error_dict)

    def get_request_json(self):
        try:
            return json.loads(self.request.body)
        except ValueError:
            raise JsonRequestError(error_message=self.error_response_message)

    def prepare(self):
        if self.json_required[self.request.method]:
            self.request_json = self.get_request_json()
            self.json_validate()
<|endoftext|>


<commit_before>from django.db import models
from datetime import datetime
import pytz
from councilmatic.city_config import TIMEZONE, OCD_CITY_COUNCIL_ID

app_timezone = pytz.timezone(TIMEZONE)
now = datetime.now()

class Person(models.Model):
    ocd_id = models.CharField(max_length=100, unique=True)
    name = models.CharField(max_length=100)
    headshot = models.CharField(max_length=255, blank=True)
    source_url = models.CharField(max_length=255)
    source_note = models.CharField(max_length=255, blank=True)
    website_url = models.CharField(max_length=255, blank=True)
    email = models.CharField(max_length=255, blank=True)
    slug = models.CharField(max_length=255, unique=True)

    def __str__(self):
        return self.name

    @property
    def council_seat(self):
        return self.memberships.filter(organization__ocd_id=OCD_CITY_COUNCIL_ID).first().post.label

    @property
    def is_speaker(self):
        return True if self.memberships.filter(role='Speaker').first() else False

    @property
    def headshot_url(self):
        if self.headshot:
            return '/static/images/' + self.ocd_id + ".jpg"
        else:
            return '/static/images/headshot_placeholder.png'

    @property
    def link_html(self):
        return ''+self.name+''

class Bill(models.Model):
    ocd_id = models.CharField(max_length=100, unique=True)
    description = models.TextField()
    identifier = models.CharField(max_length=50)
    bill_type = models.CharField(max_length=50)
    classification = models.CharField(max_length=100)
    date_created = models.DateTimeField(default=None)
    date_updated = models.DateTimeField(default=None, null=True)
    source_url = models.CharField(max_length=255)
    source_note = models.CharField(max_length=255, blank=True)
    from_organization = models.ForeignKey('Organization', related_name='bills', null=True)
    full_text = models.TextField(blank=True)
    abstract = models.TextField(blank=True)
    last_action_date = models.DateTimeField(default=None, null=True)
    legislative_session = models.ForeignKey('LegislativeSession', related_name='bills', null=True)
    slug = models.CharField(max_length=255, unique=True)

    def __str__(self):
        return self.friendly_name

    @property
    def controlling_body(self):
        if self.current_action:
            related_orgs = self.current_action.related_entities.filter(entity_type='organization').all()
            if related_orgs:
                controlling_bodies = [Organization.objects.all().filter(ocd_id=org.organization_ocd_id).first() for org in related_orgs]
                return controlling_bodies
            else:
                return [self.current_action.organization]
        else:
            return None

    @property
    def last_action_org(self):
        return self.current_action.organization if self.current_action else None

    @property
    def current_action(self):
        return self.actions.all().order_by('-order').first() if self.actions.all() else None

    @property
    def date_passed(self):
        return self.actions.filter(classification='executive-signature').order_by('-order').first().date if self.actions.all() else None

    @property
    def friendly_name(self):
        nums_only = self.identifier.split(' ')[-1]
        return self.bill_type+' '+nums_only

    @property
    def primary_sponsor(self):
        return self.sponsorships.filter(is_primary=True).first()

    @property
    def committees_involved(self):
        if self.actions.all():
            orgs = set([a.organization.name for a in self.actions.all() if (a.organization.name !='Mayor' and a.organization.name != 'New York City Council')])
            return list(orgs)
        else:
            return None

    @property
    def is_stale(self):
        # stale = no action for 2 months
        if self.current_action:
            timediff = datetime.now().replace(tzinfo=app_timezone) - self.current_action.date
            return (timediff.days > 60)
        else:
            return True

    @property
    def terminal_status(self):
        if self.actions:
            if self.bill_type == 'Introduction':
                if 'executive-signature' in [a.classification for a in self.actions.all()]:
                    return 'Passed'
                else:
                    return False
            elif self.bill_type in ['Resolution', 'Land Use Application', 'Communication', "Mayor's Message", 'Land Use Call-Up']:
                if 'passage' in [a.classification for a in self.actions.all()]:
                    return 'Passed'
                else:
                    return False
        else:
            return False

    @property
    def is_approved(self):
        if self.actions:
            return any(['Approved' in a.description for a in self.actions.all()])
        else:
            return False

    @property
    def inferred_status(self):
        # these are the bill types for which a status doesn't make sense
        if self.bill_type in ['SLR', 'Petition', 'Local Laws 2015']:
            return None
        elif self.terminal_status:
            return self.terminal_status
        elif self.is_stale:
            return 'Stale'
        else:
            return 'Active'

    def get_last_action_date(self):
        return self.actions.all().order_by('-order').first().date if self.actions.all() else None

class Organization(models.Model):
    ocd_id = models.CharField(max_length=100, unique=True)
    name = models.CharField(max_length=255)
    classification = models.CharField(max_length=255, null=True)
    parent = models.ForeignKey('self', related_name='children', null=True)
    slug = models.CharField(max_length=255, unique=True)

    def __str__(self):
        return self.name

    @classmethod
    def committees(cls):
        return cls.objects.filter(classification='committee').order_by('name').all()

    @property
    def recent_activity(self):
        return self.actions.order_by('-date', '-bill__identifier', '-order') if self.actions.all() else None

    @property
    def chairs(self):
        return self.memberships.filter(role='CHAIRPERSON')

    @property
    def link_html(self):
        # make link to committee if committee
        if self.classification == 'committee':
            return ''+self.name+''
        # link to the council members page if its the council
        if self.classification == 'legislature':
            return ''+self.name+''
        # just return text if executive
        else:
            return self.name

class Action(models.Model):
    date = models.DateTimeField(default=None)
    classification = models.CharField(max_length=100)
    description = models.TextField(blank=True)
    organization = models.ForeignKey('Organization', related_name='actions', null=True)
    bill = models.ForeignKey('Bill', related_name='actions', null=True)
    order = models.IntegerField()

    @property
    def label(self):
        c = self.classification

        if c == 'committee-passage': return 'success'
        if c == 'passage': return 'success'
        if c == 'executive-signature': return 'success'
        if c == 'amendment-passage': return 'success'

        if c == 'amendment-introduction': return 'info'
        if c == 'introduction': return 'info'
        if c == 'committee-referral': return 'info'
        if c == 'filing': return 'info'
        if c == 'executive-received': return 'info'

        if c == 'deferred': return 'primary'

        else: return 'info'

class ActionRelatedEntity(models.Model):
    action = models.ForeignKey('Action', related_name='related_entities')
    entity_type = models.CharField(max_length=100)
    entity_name = models.CharField(max_length=255)
    organization_ocd_id = models.CharField(max_length=100, blank=True)
    person_ocd_id = models.CharField(max_length=100, blank=True)

class Post(models.Model):
    ocd_id = models.CharField(max_length=100, unique=True)
    label = models.CharField(max_length=255)
    role = models.CharField(max_length=255)
    organization = models.ForeignKey('Organization', related_name='posts')

    @property
    def current_member(self):
        return self.memberships.order_by('-start_date').first() if self.memberships else None

class Membership(models.Model):
    organization = models.ForeignKey('Organization', related_name='memberships')
    person = models.ForeignKey('Person', related_name='memberships')
    post = models.ForeignKey('Post', related_name='memberships', null=True)
    label = models.CharField(max_length=255, blank=True)
    role = models.CharField(max_length=255, blank=True)
    start_date = models.DateField(default=None, null=True)
    end_date = models.DateField(default=None, null=True)

class Sponsorship(models.Model):
    bill = models.ForeignKey('Bill', related_name='sponsorships')
    person = models.ForeignKey('Person', related_name='sponsorships')
    classification = models.CharField(max_length=255)
    is_primary = models.BooleanField(default=False)

class Event(models.Model):
    ocd_id = models.CharField(max_length=100, unique=True)
    name = models.CharField(max_length=255)
    description = models.TextField()
    classification = models.CharField(max_length=100)
    start_time = models.DateTimeField()
    end_time = models.DateTimeField(null=True)
    all_day = models.BooleanField(default=False)
    status = models.CharField(max_length=100)
    location_name = models.CharField(max_length=255)
    location_url = models.CharField(max_length=255, blank=True)
    source_url = models.CharField(max_length=255)
    source_note = models.CharField(max_length=255, blank=True)
    slug = models.CharField(max_length=255, unique=True)

    @property
    def event_page_url(self):
        return '/event/%s' %self.slug

    @classmethod
    def next_city_council_meeting(cls):
        return cls.objects.filter(name__icontains='City Council Stated Meeting').filter(start_time__gt=now).order_by('start_time').first()

    @classmethod
    def upcoming_committee_meetings(cls):
        return cls.objects.filter(start_time__gt=now).exclude(name='City Council Stated Meeting').exclude(name='City Council Stated Meeting ').order_by('start_time').all()[:3]

class EventParticipant(models.Model):
    event = models.ForeignKey('Event', related_name='participants')
    note = models.TextField()
    entity_name = models.CharField(max_length=255)
    entity_type = models.CharField(max_length=100)

class EventAgendaItem(models.Model):
    event = models.ForeignKey('Event', related_name='agenda_items')
    order = models.IntegerField()
    description = models.TextField()

class AgendaItemBill(models.Model):
    agenda_item = models.ForeignKey('EventAgendaItem', related_name='related_bills')
    bill = models.ForeignKey('Bill', related_name='related_agenda_items')
    note = models.CharField(max_length=255)

class Document(models.Model):
    note = models.TextField()
    url = models.TextField()

class BillDocument(models.Model):
    bill = models.ForeignKey('Bill', related_name='documents')
    document = models.ForeignKey('Document', related_name='bills')

class EventDocument(models.Model):
    event = models.ForeignKey('Event', related_name='documents')
    document = models.ForeignKey('Document', related_name='events')

class LegislativeSession(models.Model):
    identifier = models.CharField(max_length=255)
    jurisdiction_ocd_id = models.CharField(max_length=255)
    name = models.CharField(max_length=255)

<commit_msg>fix logic for current council members, closes #84<commit_after>from django.db import models
from datetime import datetime
import pytz
from councilmatic.city_config import TIMEZONE, OCD_CITY_COUNCIL_ID

app_timezone = pytz.timezone(TIMEZONE)
now = datetime.now()

class Person(models.Model):
    ocd_id = models.CharField(max_length=100, unique=True)
    name = models.CharField(max_length=100)
    headshot = models.CharField(max_length=255, blank=True)
    source_url = models.CharField(max_length=255)
    source_note = models.CharField(max_length=255, blank=True)
    website_url = models.CharField(max_length=255, blank=True)
    email = models.CharField(max_length=255, blank=True)
    slug = models.CharField(max_length=255, unique=True)

    def __str__(self):
        return self.name

    @property
    def council_seat(self):
        return self.memberships.filter(organization__ocd_id=OCD_CITY_COUNCIL_ID).first().post.label

    @property
    def is_speaker(self):
        return True if self.memberships.filter(role='Speaker').first() else False

    @property
    def headshot_url(self):
        if self.headshot:
            return '/static/images/' + self.ocd_id + ".jpg"
        else:
            return '/static/images/headshot_placeholder.png'

    @property
    def link_html(self):
        return ''+self.name+''

class Bill(models.Model):
    ocd_id = models.CharField(max_length=100, unique=True)
    description = models.TextField()
    identifier = models.CharField(max_length=50)
    bill_type = models.CharField(max_length=50)
    classification = models.CharField(max_length=100)
    date_created = models.DateTimeField(default=None)
    date_updated = models.DateTimeField(default=None, null=True)
    source_url = models.CharField(max_length=255)
    source_note = models.CharField(max_length=255, blank=True)
    from_organization = models.ForeignKey('Organization', related_name='bills', null=True)
    full_text = models.TextField(blank=True)
    abstract = models.TextField(blank=True)
    last_action_date = models.DateTimeField(default=None, null=True)
    legislative_session = models.ForeignKey('LegislativeSession', related_name='bills', null=True)
    slug = models.CharField(max_length=255, unique=True)

    def __str__(self):
        return self.friendly_name

    @property
    def controlling_body(self):
        if self.current_action:
            related_orgs = self.current_action.related_entities.filter(entity_type='organization').all()
            if related_orgs:
                controlling_bodies = [Organization.objects.all().filter(ocd_id=org.organization_ocd_id).first() for org in related_orgs]
                return controlling_bodies
            else:
                return [self.current_action.organization]
        else:
            return None

    @property
    def last_action_org(self):
        return self.current_action.organization if self.current_action else None

    @property
    def current_action(self):
        return self.actions.all().order_by('-order').first() if self.actions.all() else None

    @property
    def date_passed(self):
        return self.actions.filter(classification='executive-signature').order_by('-order').first().date if self.actions.all() else None

    @property
    def friendly_name(self):
        nums_only = self.identifier.split(' ')[-1]
        return self.bill_type+' '+nums_only

    @property
    def primary_sponsor(self):
        return self.sponsorships.filter(is_primary=True).first()

    @property
    def committees_involved(self):
        if self.actions.all():
            orgs = set([a.organization.name for a in self.actions.all() if (a.organization.name !='Mayor' and a.organization.name != 'New York City Council')])
            return list(orgs)
        else:
            return None

    @property
    def is_stale(self):
        # stale = no action for 2 months
        if self.current_action:
            timediff = datetime.now().replace(tzinfo=app_timezone) - self.current_action.date
            return (timediff.days > 60)
        else:
            return True

    @property
    def terminal_status(self):
        if self.actions:
            if self.bill_type == 'Introduction':
                if 'executive-signature' in [a.classification for a in self.actions.all()]:
                    return 'Passed'
                else:
                    return False
            elif self.bill_type in ['Resolution', 'Land Use Application', 'Communication', "Mayor's Message", 'Land Use Call-Up']:
                if 'passage' in [a.classification for a in self.actions.all()]:
                    return 'Passed'
                else:
                    return False
        else:
            return False

    @property
    def is_approved(self):
        if self.actions:
            return any(['Approved' in a.description for a in self.actions.all()])
        else:
            return False

    @property
    def inferred_status(self):
        # these are the bill types for which a status doesn't make sense
        if self.bill_type in ['SLR', 'Petition', 'Local Laws 2015']:
            return None
        elif self.terminal_status:
            return self.terminal_status
        elif self.is_stale:
            return 'Stale'
        else:
            return 'Active'

    def get_last_action_date(self):
        return self.actions.all().order_by('-order').first().date if self.actions.all() else None

class Organization(models.Model):
    ocd_id = models.CharField(max_length=100, unique=True)
    name = models.CharField(max_length=255)
    classification = models.CharField(max_length=255, null=True)
    parent = models.ForeignKey('self', related_name='children', null=True)
    slug = models.CharField(max_length=255, unique=True)

    def __str__(self):
        return self.name

    @classmethod
    def committees(cls):
        return cls.objects.filter(classification='committee').order_by('name').all()

    @property
    def recent_activity(self):
        return self.actions.order_by('-date', '-bill__identifier', '-order') if self.actions.all() else None

    @property
    def chairs(self):
        return self.memberships.filter(role='CHAIRPERSON')

    @property
    def link_html(self):
        # make link to committee if committee
        if self.classification == 'committee':
            return ''+self.name+''
        # link to the council members page if its the council
        if self.classification == 'legislature':
            return ''+self.name+''
        # just return text if executive
        else:
            return self.name

class Action(models.Model):
    date = models.DateTimeField(default=None)
    classification = models.CharField(max_length=100)
    description = models.TextField(blank=True)
    organization = models.ForeignKey('Organization', related_name='actions', null=True)
    bill = models.ForeignKey('Bill', related_name='actions', null=True)
    order = models.IntegerField()

    @property
    def label(self):
        c = self.classification

        if c == 'committee-passage': return 'success'
        if c == 'passage': return 'success'
        if c == 'executive-signature': return 'success'
        if c == 'amendment-passage': return 'success'

        if c == 'amendment-introduction': return 'info'
        if c == 'introduction': return 'info'
        if c == 'committee-referral': return 'info'
        if c == 'filing': return 'info'
        if c == 'executive-received': return 'info'

        if c == 'deferred': return 'primary'

        else: return 'info'

class ActionRelatedEntity(models.Model):
    action = models.ForeignKey('Action', related_name='related_entities')
    entity_type = models.CharField(max_length=100)
    entity_name = models.CharField(max_length=255)
    organization_ocd_id = models.CharField(max_length=100, blank=True)
    person_ocd_id = models.CharField(max_length=100, blank=True)

class Post(models.Model):
    ocd_id = models.CharField(max_length=100, unique=True)
    label = models.CharField(max_length=255)
    role = models.CharField(max_length=255)
    organization = models.ForeignKey('Organization', related_name='posts')

    @property
    def current_member(self):
        if self.memberships:
            most_recent_member = self.memberships.order_by('-start_date').first()
            if most_recent_member.end_date:
                return None
            else:
                return most_recent_member
        else:
            return None

class Membership(models.Model):
    organization = models.ForeignKey('Organization', related_name='memberships')
    person = models.ForeignKey('Person', related_name='memberships')
    post = models.ForeignKey('Post', related_name='memberships', null=True)
    label = models.CharField(max_length=255, blank=True)
    role = models.CharField(max_length=255, blank=True)
    start_date = models.DateField(default=None, null=True)
    end_date = models.DateField(default=None, null=True)

class Sponsorship(models.Model):
    bill = models.ForeignKey('Bill', related_name='sponsorships')
    person = models.ForeignKey('Person', related_name='sponsorships')
    classification = models.CharField(max_length=255)
    is_primary = models.BooleanField(default=False)

class Event(models.Model):
    ocd_id = models.CharField(max_length=100, unique=True)
    name = models.CharField(max_length=255)
    description = models.TextField()
    classification = models.CharField(max_length=100)
    start_time = models.DateTimeField()
    end_time = models.DateTimeField(null=True)
    all_day = models.BooleanField(default=False)
    status = models.CharField(max_length=100)
    location_name = models.CharField(max_length=255)
    location_url = models.CharField(max_length=255, blank=True)
    source_url = models.CharField(max_length=255)
    source_note = models.CharField(max_length=255, blank=True)
    slug = models.CharField(max_length=255, unique=True)

    @property
    def event_page_url(self):
        return '/event/%s' %self.slug

    @classmethod
    def next_city_council_meeting(cls):
        return cls.objects.filter(name__icontains='City Council Stated Meeting').filter(start_time__gt=now).order_by('start_time').first()

    @classmethod
    def upcoming_committee_meetings(cls):
        return cls.objects.filter(start_time__gt=now).exclude(name='City Council Stated Meeting').exclude(name='City Council Stated Meeting ').order_by('start_time').all()[:3]

class EventParticipant(models.Model):
    event = models.ForeignKey('Event', related_name='participants')
    note = models.TextField()
    entity_name = models.CharField(max_length=255)
    entity_type = models.CharField(max_length=100)

class EventAgendaItem(models.Model):
    event = models.ForeignKey('Event', related_name='agenda_items')
    order = models.IntegerField()
    description = models.TextField()

class AgendaItemBill(models.Model):
    agenda_item = models.ForeignKey('EventAgendaItem', related_name='related_bills')
    bill = models.ForeignKey('Bill', related_name='related_agenda_items')
    note = models.CharField(max_length=255)

class Document(models.Model):
    note = models.TextField()
    url = models.TextField()

class BillDocument(models.Model):
    bill = models.ForeignKey('Bill', related_name='documents')
    document = models.ForeignKey('Document', related_name='bills')

class EventDocument(models.Model):
    event = models.ForeignKey('Event', related_name='documents')
    document = models.ForeignKey('Document', related_name='events')

class LegislativeSession(models.Model):
    identifier = models.CharField(max_length=255)
    jurisdiction_ocd_id = models.CharField(max_length=255)
    name = models.CharField(max_length=255)

<|endoftext|>


<commit_before>import datetime
import simplejson

from django.contrib.auth.models import User
from django.db.models import Model
from django.db.models import (CharField, DateTimeField, FloatField, ForeignKey,
                              TextField)

from core.commands import execute_command

STATUS_CHOICES = (
    ("private", "private"),
    ("public", "public")
)

class Command(Model):
    created_by = ForeignKey(User, related_name="created_commands")
    created_at = DateTimeField(auto_now=True)
    lat = FloatField(null=True)
    lng = FloatField(null=True)
    raw_command = TextField(blank=True)
    name = CharField(blank=True, max_length=256)
    data = CharField(blank=True, max_length=1024)
    response = TextField(blank=True)

    def save(self, *args, **kwargs):
        if not self.data:
            self.data = "{}"
        if type(self.data) == dict:
            self.data = simplejson.dumps(self.data)
        self.name, self.response = execute_command(self)
        super(Command, self).save(*args, **kwargs)

class Log(Model):
    created_by = ForeignKey(User, related_name="created_logs")
    created_at = DateTimeField(auto_now=True)
    lat = FloatField(null=True)
    lng = FloatField(null=True)
    raw_data = TextField(blank=True)

class Page(Model):
    created_by = ForeignKey(User, related_name="created_pages")
    created_at = DateTimeField()
    modified_at = DateTimeField(editable=False)
    status = CharField(
        max_length=16, choices=STATUS_CHOICES, default="private")
    title = CharField(blank=True, max_length=256)
    slug = CharField(blank=False, unique=True, max_length=256)
    head = TextField(blank=True)
    body = TextField(blank=True)
    foot = TextField(blank=True)

    def save(self, *args, **kwargs):
        if not self.id:
            self.created_at = datetime.datetime.now()
        self.modified_at = datetime.datetime.now()
        return super(Page, self).save(*args, **kwargs)

# Import receivers at the bottom to avoid circular imports
import receivers  # NOQA
<commit_msg>Added Page.variables field<commit_after>import datetime
import simplejson

from django.contrib.auth.models import User
from django.db.models import Model
from django.db.models import (CharField, DateTimeField, FloatField, ForeignKey,
                              TextField)

from core.commands import execute_command

STATUS_CHOICES = (
    ("private", "private"),
    ("public", "public")
)

class Command(Model):
    created_by = ForeignKey(User, related_name="created_commands")
    created_at = DateTimeField(auto_now=True)
    lat = FloatField(null=True)
    lng = FloatField(null=True)
    raw_command = TextField(blank=True)
    name = CharField(blank=True, max_length=256)
    data = CharField(blank=True, max_length=1024)
    response = TextField(blank=True)

    def save(self, *args, **kwargs):
        if not self.data:
            self.data = "{}"
        if type(self.data) == dict:
            self.data = simplejson.dumps(self.data)
        self.name, self.response = execute_command(self)
        super(Command, self).save(*args, **kwargs)

class Log(Model):
    created_by = ForeignKey(User, related_name="created_logs")
    created_at = DateTimeField(auto_now=True)
    lat = FloatField(null=True)
    lng = FloatField(null=True)
    raw_data = TextField(blank=True)

class Page(Model):
    created_by = ForeignKey(User, related_name="created_pages")
    created_at = DateTimeField()
    modified_at = DateTimeField(editable=False)
    status = CharField(
        max_length=16, choices=STATUS_CHOICES, default="private")
    title = CharField(blank=True, max_length=256)
    slug = CharField(blank=False, unique=True, max_length=256)
    head = TextField(blank=True)
    body = TextField(blank=True)
    foot = TextField(blank=True)
    variables = TextField(blank=True)

    def save(self, *args, **kwargs):
        if not self.id:
            self.created_at = datetime.datetime.now()
        self.modified_at = datetime.datetime.now()
        return super(Page, self).save(*args, **kwargs)

# Import receivers at the bottom to avoid circular imports
import receivers  # NOQA
<|endoftext|>


<commit_before>#
# Copyright (c) 2006-2007 rPath, Inc.  All Rights Reserved.
#
import errno
import os
import socket
import sys
import tempfile
import time
import traceback

from conary import conaryclient
from conary.lib import util
from conary.repository import changeset

from rmake import errors

from rmake import failure
from rmake.build import subscriber
from rmake.worker import resolver

from rmake.lib import logfile
from rmake.lib import logger
from rmake.lib import repocache
from rmake.lib import server

from rmake.lib.apiutils import thaw, freeze

class Command(server.Server):
    name = 'command'
    # handles one job then exists.
    def __init__(self, cfg, commandId, jobId):
        server.Server.__init__(self)
        self.cfg = cfg
        self.commandId = commandId
        self.jobId = jobId
        self._isErrored = False
        self._errorMessage = ''
        self._output = []
        self.readPipe = None

    def _exit(self, exitRc):
        sys.exit(exitRc)

    def shouldFork(self):
        return True

    def setWritePipe(self, writePipe):
        self.writePipe = writePipe

    def setReadPipe(self, readPipe):
        self.readPipe = readPipe

    def fileno(self):
        return self.readPipe.fileno()

    def handleRead(self):
        data = self.readPipe.handle_read()
        if data:
            self._handleData(data)

    def flushInputBuffer(self):
        if not self.readPipe:
            return
        for data in self.readPipe.readUntilClosed():
            self._handleData(data)

    def handleWrite(self):
        self.writePipe.handle_write()

    def _handleData(self, data):
        self._output.append(data)

    def getCommandId(self):
        return self.commandId

    def getChrootFactory(self):
        return None

    def getLogPath(self):
        commandId = (self.getCommandId().replace('/', '_')
                                                    .replace('~', '')
                                                    .replace(' ', ''))
        base =  self.cfg.getBuildLogDir(self.jobId)
        return '%s/%s.log' % (base, commandId)

    def runCommandAndExit(self):
        self._installSignalHandlers()
        try:
            self._try('Command', self._runCommand)
            os._exit(0)
        except SystemExit, err:
            os._exit(err.args[0])
        except:
            # error occurred, but we'll let our parent node send out the
            # "errored" message.
            os._exit(1)

    def _serveLoopHook(self):
        self.writePipe.handleWriteIfReady()
        self._collectChildren()

    def runCommandNoExit(self):
        try:
            self._try('Command', self._runCommand)
        except SystemExit, err:
            return err.args[0]
        return 0

    def _runCommand(self):
        self.commandStarted()
        self.logger = logger.Logger(self.name, self.getLogPath())
        self.logFile = logfile.LogFile(self.getLogPath())
        self.logFile.redirectOutput()
        try:
            self.logger.info('Running Command... (pid %s)' % os.getpid())
            try:
                self.runCommand()
            except SystemExit, err:
                raise
            except Exception, err:
                if isinstance(err, SystemExit) and not err.args[0]:
                    self.commandFinished()
                else:
                    self.commandErrored(err, traceback.format_exc())
                raise
            else:
                self.commandFinished()
        finally:
            self.logFile.restoreOutput()

    def isErrored(self):
        return self._isErrored

    def getFailureReason(self):
        return self.failureReason

    def setError(self, msg, tb=''):
        self._isErrored = True
        self.failureReason = failure.CommandFailed(self.commandId, str(msg), tb)

    def commandDied(self, status):
        self.flushInputBuffer()
        exitRc = os.WEXITSTATUS(status)
        signalRc = os.WTERMSIG(status)
        commandId = self.getCommandId()
        if exitRc or signalRc:
            if exitRc:
                msg = 'unexpectedly died with exit code %s'
                msg = msg % (exitRc)
            else:
                msg = 'unexpectedly killed with signal %s'
                msg = msg % (signalRc)
            self.setError(msg)
        self.commandFinished()

    def commandStarted(self):
        pass

    def commandFinished(self):
        pass

    def commandErrored(self, msg, tb=''):
        self.setError(msg, tb)

class TroveCommand(Command):

    def __init__(self, serverCfg, commandId, jobId, eventHandler, trove):
        Command.__init__(self, serverCfg, commandId, jobId)
        self.eventHandler = eventHandler
        self.trove = trove
        self.readPipe = None
        self.failureReason = None

    def _signalHandler(self, signal, frame):
        failureReason = failure.Stopped('Signal %s received' % signal)
        self.trove.troveFailed(failureReason)
        Command._signalHandler(self, signal, frame)

    def _handleData(self, (jobId, eventList)):
        self.eventHandler._receiveEvents(*thaw('EventList', eventList))

    def getTrove(self):
        return self.trove

    def runCommand(self):
        try:
            trove = self.trove
            trove.getPublisher().reset()
            PipePublisher(self.writePipe).attach(trove)
            self.runTroveCommand()
        except SystemExit, err:
            self.writePipe.flush()
            raise
        except Exception, err:
            self.writePipe.flush()
            # even if there's an exception in here, we don't really
            # want to retry the build.
            reason = failure.InternalError(str(err), traceback.format_exc())
            trove.troveFailed(reason)
            raise

    def handleRequestIfReady(self, sleep):
        time.sleep(sleep)

    def _serveLoopHook(self):
        try:
            self.writePipe.handleWriteIfReady()
            self._troveServeLoopHook()
        except SystemExit, err:
            raise
        except Exception, err:
            reason = failure.InternalError(str(err), traceback.format_exc())
            self.getTrove().troveFailed(reason)
            raise

class BuildCommand(TroveCommand):

    name = 'build-command'

    def __init__(self, serverCfg, commandId, jobId, eventHandler, buildCfg,
                 chrootFactory, trove, targetLabel, logHost='', logPort=0,
                 logPath=None, uri=None):
        TroveCommand.__init__(self, serverCfg, commandId, jobId, eventHandler,
                              trove)
        self.buildCfg = buildCfg
        self.chrootFactory = chrootFactory
        self.targetLabel = targetLabel
        self.logHost = logHost
        self.logPort = logPort
        self.logPath = logPath
        self.uri = None

    def _signalHandler(self, signal, frame):
        failureReason = failure.Stopped('Signal %s received' % signal)
        self.trove.troveFailed(failureReason)
        Command._signalHandler(self, signal, frame)

    def _handleData(self, (jobId, eventList)):
        self.eventHandler._receiveEvents(*thaw('EventList', eventList))

    def getTrove(self):
        return self.trove

    def getChrootFactory(self):
        return self.chrootFactory

    def runTroveCommand(self):
        try:
            trove = self.trove
            trove.getPublisher().reset()
            PipePublisher(self.writePipe).attach(trove)
            trove.creatingChroot(self.cfg.getName(),
                                 self.chrootFactory.getChrootName())
            self.chrootFactory.create()
            self.chroot = self.chrootFactory.start()
        except Exception, err:
            # sends off messages to all listeners that this trove failed.
            trove.chrootFailed(str(err), traceback.format_exc())
            return
        n,v,f = trove.getNameVersionFlavor()
        logPath, pid = self.chroot.buildTrove(self.buildCfg,
                                              self.targetLabel,
                                              n, v, f, self.logHost,
                                              self.logPort)
        # sends off message that this trove is building.
        self.chroot.subscribeToBuild(n,v,f)
        if self.logPath:
            logPath = self.logPath
        trove.troveBuilding(logPath, pid)
        self.serve_forever()

    def _troveServeLoopHook(self):
        if self.chroot.checkSubscription():
            self.getResults()
            self._halt = True
            return

    def getResults(self):
        try:
            trove = self.trove
            repos = conaryclient.ConaryClient(self.buildCfg).getRepos()
            buildResult = self.chroot.checkResults(
                                            *self.trove.getNameVersionFlavor())
            if buildResult.isBuildSuccess():
                csFile = buildResult.getChangeSetFile()
                cs = changeset.ChangeSetFromFile(csFile)
                repos.commitChangeSet(cs)
                # sends off message that this trove built successfully
                troveList = [x.getNewNameVersionFlavor() for
                             x in cs.iterNewTroveList() ]
                trove.troveBuilt(troveList)
                del cs # this makes sure the changeset closes the fd.
            else:
                reason = buildResult.getFailureReason()
                trove.troveFailed(reason)
                # passes through to killRoot at the bottom.
        except Exception, e:
            reason = failure.InternalError(str(e), traceback.format_exc())
            trove.troveFailed(reason)
        self.chroot.stop()

    def _shutDown(self):
        try:
            self.chroot.stop()
        except OSError, err:
            if err.errno != errno.ESRCH:
                raise
            else:
                return
        except errors.OpenError, err:
            pass
        self._killPid(self.chroot.pid)

        if self.buildCfg.cleanAfterCook and self.trove.isBuilt():
            self.chrootFactory.clean()
        else:
            self.chrootFactory.unmount()
        Command._shutDown(self)

class StopCommand(Command):

    name = 'stop-command'

    def __init__(self, cfg, commandId, targetCommand, killFn, hook):
        Command.__init__(self, cfg, commandId, targetCommand.jobId)
        self.targetCommand = targetCommand
        self.killFn = killFn
        self.hook = hook

    def runCommand(self):
        self.killFn(self.targetCommand.pid, hook=self.hook)

    def shouldFork(self):
        # Because this command runs kill, it must be the parent process
        # that runs kill, not some other child.  You can't kill your sibling.
        return False

    def commandDied(self, status):
        Command.commandDied(self, status)
        try:
            pid, status = os.waitpid(self.targetCommand.pid, os.WNOHANG)
        except OSError, err:
            if err.errno == errno.ECHILD:
                pass
            elif err.errno == errno.ESRCH:
                pass
            else:
                raise
        else:
            self.setError('%s did not die!' % self.targetCommand.getCommandId())

class SessionCommand(Command):

    name = 'session-command'

    def __init__(self, serverCfg, commandId, chrootFactory, command):
        Command.__init__(self, serverCfg, commandId, 0)
        self.chrootFactory = chrootFactory
        self.command = command
        self.hostInfo = []

    def getTrove(self):
        return self.trove

    def getChrootFactory(self):
        return self.chrootFactory

    def runCommand(self):
        self.chrootFactory.create()
        self.chroot = self.chrootFactory.start()
        port = self.chroot.startSession(self.command)
        self.writePipe.send((socket.getfqdn(), port))
        self.writePipe.flush()
        self.serve_forever()

    def _pidDied(self, pid, status, name=None):
        if pid == self.chroot.pid:
            self.chrootFactory.unmount()
            self._halt = True

    def _handleData(self, output):
        self.hostInfo = output

    def getHostInfo(self):
        return self.hostInfo

class ResolveCommand(TroveCommand):

    name = 'resolve-command'

    def __init__(self, cfg, commandId, jobId,  eventHandler, resolveJob):
        TroveCommand.__init__(self, cfg, commandId, jobId, eventHandler,
                              resolveJob.getTrove())
        self.resolveJob = resolveJob

    def runTroveCommand(self):
        client = conaryclient.ConaryClient(self.resolveJob.getConfig())
        repos = repocache.CachingTroveSource(client.getRepos(),
                                             self.cfg.getCacheDir())
        self.resolver = resolver.DependencyResolver(self.logger, repos)
        resolveResult = self.resolver.resolve(self.resolveJob)
        self.trove.troveResolved(resolveResult)

class PipePublisher(subscriber._RmakePublisherProxy):
    """
        Class that transmits events from internal build process -> rMake server.
    """

    # we override the _receiveEvents method to just pass these
    # events on, thus we just use listeners as a list of states we subscribe to

    def __init__(self, pipeWriter):
        self.pipeWriter = pipeWriter
        subscriber._RmakePublisherProxy.__init__(self)

    def _emitEvents(self, jobId, eventList):
        self.pipeWriter.send((jobId, freeze('EventList', eventList)))
        self.pipeWriter.flush()

def attach(trove, p):
    _RmakeBusPublisher(client).attach(trove)

<commit_msg>send resolving build reqs message<commit_after>#
# Copyright (c) 2006-2007 rPath, Inc.  All Rights Reserved.
#
import errno
import os
import socket
import sys
import tempfile
import time
import traceback

from conary import conaryclient
from conary.lib import util
from conary.repository import changeset

from rmake import errors

from rmake import failure
from rmake.build import subscriber
from rmake.worker import resolver

from rmake.lib import logfile
from rmake.lib import logger
from rmake.lib import repocache
from rmake.lib import server

from rmake.lib.apiutils import thaw, freeze

class Command(server.Server):
    name = 'command'
    # handles one job then exists.
    def __init__(self, cfg, commandId, jobId):
        server.Server.__init__(self)
        self.cfg = cfg
        self.commandId = commandId
        self.jobId = jobId
        self._isErrored = False
        self._errorMessage = ''
        self._output = []
        self.readPipe = None

    def _exit(self, exitRc):
        sys.exit(exitRc)

    def shouldFork(self):
        return True

    def setWritePipe(self, writePipe):
        self.writePipe = writePipe

    def setReadPipe(self, readPipe):
        self.readPipe = readPipe

    def fileno(self):
        return self.readPipe.fileno()

    def handleRead(self):
        data = self.readPipe.handle_read()
        if data:
            self._handleData(data)

    def flushInputBuffer(self):
        if not self.readPipe:
            return
        for data in self.readPipe.readUntilClosed():
            self._handleData(data)

    def handleWrite(self):
        self.writePipe.handle_write()

    def _handleData(self, data):
        self._output.append(data)

    def getCommandId(self):
        return self.commandId

    def getChrootFactory(self):
        return None

    def getLogPath(self):
        commandId = (self.getCommandId().replace('/', '_')
                                                    .replace('~', '')
                                                    .replace(' ', ''))
        base =  self.cfg.getBuildLogDir(self.jobId)
        return '%s/%s.log' % (base, commandId)

    def runCommandAndExit(self):
        self._installSignalHandlers()
        try:
            self._try('Command', self._runCommand)
            os._exit(0)
        except SystemExit, err:
            os._exit(err.args[0])
        except:
            # error occurred, but we'll let our parent node send out the
            # "errored" message.
            os._exit(1)

    def _serveLoopHook(self):
        self.writePipe.handleWriteIfReady()
        self._collectChildren()

    def runCommandNoExit(self):
        try:
            self._try('Command', self._runCommand)
        except SystemExit, err:
            return err.args[0]
        return 0

    def _runCommand(self):
        self.commandStarted()
        self.logger = logger.Logger(self.name, self.getLogPath())
        self.logFile = logfile.LogFile(self.getLogPath())
        self.logFile.redirectOutput()
        try:
            self.logger.info('Running Command... (pid %s)' % os.getpid())
            try:
                self.runCommand()
            except SystemExit, err:
                raise
            except Exception, err:
                if isinstance(err, SystemExit) and not err.args[0]:
                    self.commandFinished()
                else:
                    self.commandErrored(err, traceback.format_exc())
                raise
            else:
                self.commandFinished()
        finally:
            self.logFile.restoreOutput()

    def isErrored(self):
        return self._isErrored

    def getFailureReason(self):
        return self.failureReason

    def setError(self, msg, tb=''):
        self._isErrored = True
        self.failureReason = failure.CommandFailed(self.commandId, str(msg), tb)

    def commandDied(self, status):
        self.flushInputBuffer()
        exitRc = os.WEXITSTATUS(status)
        signalRc = os.WTERMSIG(status)
        commandId = self.getCommandId()
        if exitRc or signalRc:
            if exitRc:
                msg = 'unexpectedly died with exit code %s'
                msg = msg % (exitRc)
            else:
                msg = 'unexpectedly killed with signal %s'
                msg = msg % (signalRc)
            self.setError(msg)
        self.commandFinished()

    def commandStarted(self):
        pass

    def commandFinished(self):
        pass

    def commandErrored(self, msg, tb=''):
        self.setError(msg, tb)

class TroveCommand(Command):

    def __init__(self, serverCfg, commandId, jobId, eventHandler, trove):
        Command.__init__(self, serverCfg, commandId, jobId)
        self.eventHandler = eventHandler
        self.trove = trove
        self.readPipe = None
        self.failureReason = None

    def _signalHandler(self, signal, frame):
        failureReason = failure.Stopped('Signal %s received' % signal)
        self.trove.troveFailed(failureReason)
        Command._signalHandler(self, signal, frame)

    def _handleData(self, (jobId, eventList)):
        self.eventHandler._receiveEvents(*thaw('EventList', eventList))

    def getTrove(self):
        return self.trove

    def runCommand(self):
        try:
            trove = self.trove
            trove.getPublisher().reset()
            PipePublisher(self.writePipe).attach(trove)
            self.runTroveCommand()
        except SystemExit, err:
            self.writePipe.flush()
            raise
        except Exception, err:
            self.writePipe.flush()
            # even if there's an exception in here, we don't really
            # want to retry the build.
            reason = failure.InternalError(str(err), traceback.format_exc())
            trove.troveFailed(reason)
            raise

    def handleRequestIfReady(self, sleep):
        time.sleep(sleep)

    def _serveLoopHook(self):
        try:
            self.writePipe.handleWriteIfReady()
            self._troveServeLoopHook()
        except SystemExit, err:
            raise
        except Exception, err:
            reason = failure.InternalError(str(err), traceback.format_exc())
            self.getTrove().troveFailed(reason)
            raise

class BuildCommand(TroveCommand):

    name = 'build-command'

    def __init__(self, serverCfg, commandId, jobId, eventHandler, buildCfg,
                 chrootFactory, trove, targetLabel, logHost='', logPort=0,
                 logPath=None, uri=None):
        TroveCommand.__init__(self, serverCfg, commandId, jobId, eventHandler,
                              trove)
        self.buildCfg = buildCfg
        self.chrootFactory = chrootFactory
        self.targetLabel = targetLabel
        self.logHost = logHost
        self.logPort = logPort
        self.logPath = logPath
        self.uri = None

    def _signalHandler(self, signal, frame):
        failureReason = failure.Stopped('Signal %s received' % signal)
        self.trove.troveFailed(failureReason)
        Command._signalHandler(self, signal, frame)

    def _handleData(self, (jobId, eventList)):
        self.eventHandler._receiveEvents(*thaw('EventList', eventList))

    def getTrove(self):
        return self.trove

    def getChrootFactory(self):
        return self.chrootFactory

    def runTroveCommand(self):
        try:
            trove = self.trove
            trove.creatingChroot(self.cfg.getName(),
                                 self.chrootFactory.getChrootName())
            self.chrootFactory.create()
            self.chroot = self.chrootFactory.start()
        except Exception, err:
            # sends off messages to all listeners that this trove failed.
            trove.chrootFailed(str(err), traceback.format_exc())
            return
        n,v,f = trove.getNameVersionFlavor()
        logPath, pid = self.chroot.buildTrove(self.buildCfg,
                                              self.targetLabel,
                                              n, v, f, self.logHost,
                                              self.logPort)
        # sends off message that this trove is building.
        self.chroot.subscribeToBuild(n,v,f)
        if self.logPath:
            logPath = self.logPath
        trove.troveBuilding(logPath, pid)
        self.serve_forever()

    def _troveServeLoopHook(self):
        if self.chroot.checkSubscription():
            self.getResults()
            self._halt = True
            return

    def getResults(self):
        try:
            trove = self.trove
            repos = conaryclient.ConaryClient(self.buildCfg).getRepos()
            buildResult = self.chroot.checkResults(
                                            *self.trove.getNameVersionFlavor())
            if buildResult.isBuildSuccess():
                csFile = buildResult.getChangeSetFile()
                cs = changeset.ChangeSetFromFile(csFile)
                repos.commitChangeSet(cs)
                # sends off message that this trove built successfully
                troveList = [x.getNewNameVersionFlavor() for
                             x in cs.iterNewTroveList() ]
                trove.troveBuilt(troveList)
                del cs # this makes sure the changeset closes the fd.
            else:
                reason = buildResult.getFailureReason()
                trove.troveFailed(reason)
                # passes through to killRoot at the bottom.
        except Exception, e:
            reason = failure.InternalError(str(e), traceback.format_exc())
            trove.troveFailed(reason)
        self.chroot.stop()

    def _shutDown(self):
        try:
            self.chroot.stop()
        except OSError, err:
            if err.errno != errno.ESRCH:
                raise
            else:
                return
        except errors.OpenError, err:
            pass
        self._killPid(self.chroot.pid)

        if self.buildCfg.cleanAfterCook and self.trove.isBuilt():
            self.chrootFactory.clean()
        else:
            self.chrootFactory.unmount()
        Command._shutDown(self)

class StopCommand(Command):

    name = 'stop-command'

    def __init__(self, cfg, commandId, targetCommand, killFn, hook):
        Command.__init__(self, cfg, commandId, targetCommand.jobId)
        self.targetCommand = targetCommand
        self.killFn = killFn
        self.hook = hook

    def runCommand(self):
        self.killFn(self.targetCommand.pid, hook=self.hook)

    def shouldFork(self):
        # Because this command runs kill, it must be the parent process
        # that runs kill, not some other child.  You can't kill your sibling.
        return False

    def commandDied(self, status):
        Command.commandDied(self, status)
        try:
            pid, status = os.waitpid(self.targetCommand.pid, os.WNOHANG)
        except OSError, err:
            if err.errno == errno.ECHILD:
                pass
            elif err.errno == errno.ESRCH:
                pass
            else:
                raise
        else:
            self.setError('%s did not die!' % self.targetCommand.getCommandId())

class SessionCommand(Command):

    name = 'session-command'

    def __init__(self, serverCfg, commandId, chrootFactory, command):
        Command.__init__(self, serverCfg, commandId, 0)
        self.chrootFactory = chrootFactory
        self.command = command
        self.hostInfo = []

    def getTrove(self):
        return self.trove

    def getChrootFactory(self):
        return self.chrootFactory

    def runCommand(self):
        self.chrootFactory.create()
        self.chroot = self.chrootFactory.start()
        port = self.chroot.startSession(self.command)
        self.writePipe.send((socket.getfqdn(), port))
        self.writePipe.flush()
        self.serve_forever()

    def _pidDied(self, pid, status, name=None):
        if pid == self.chroot.pid:
            self.chrootFactory.unmount()
            self._halt = True

    def _handleData(self, output):
        self.hostInfo = output

    def getHostInfo(self):
        return self.hostInfo

class ResolveCommand(TroveCommand):

    name = 'resolve-command'

    def __init__(self, cfg, commandId, jobId,  eventHandler, resolveJob):
        TroveCommand.__init__(self, cfg, commandId, jobId, eventHandler,
                              resolveJob.getTrove())
        self.resolveJob = resolveJob

    def runTroveCommand(self):
        self.trove.troveResolvingBuildReqs(self.cfg.getName())
        client = conaryclient.ConaryClient(self.resolveJob.getConfig())
        repos = repocache.CachingTroveSource(client.getRepos(),
                                             self.cfg.getCacheDir())
        self.resolver = resolver.DependencyResolver(self.logger, repos)
        resolveResult = self.resolver.resolve(self.resolveJob)
        self.trove.troveResolved(resolveResult)

class PipePublisher(subscriber._RmakePublisherProxy):
    """
        Class that transmits events from internal build process -> rMake server.
    """

    # we override the _receiveEvents method to just pass these
    # events on, thus we just use listeners as a list of states we subscribe to

    def __init__(self, pipeWriter):
        self.pipeWriter = pipeWriter
        subscriber._RmakePublisherProxy.__init__(self)

    def _emitEvents(self, jobId, eventList):
        self.pipeWriter.send((jobId, freeze('EventList', eventList)))
        self.pipeWriter.flush()

def attach(trove, p):
    _RmakeBusPublisher(client).attach(trove)

<|endoftext|>