async def test_photo_rotation(monkeypatch, cli, db_conn, company,
                              image_download_url, tmpdir, other_server,
                              worker):
    monkeypatch.setattr(boto3, 'client', fake_s3_client(tmpdir))
    r = await signed_request(
        cli,
        f'/{company.public_key}/webhook/contractor',
        id=123,
        first_name='Fred',
        photo=f'{image_download_url}?exif=1',
    )
    assert r.status == 201, await r.text()
    await worker.run_check()
    assert other_server.app['request_log'] == [('test_image', None)]

    assert [
        cs.first_name
        async for cs in await db_conn.execute(sa_contractors.select())
    ] == ['Fred']
    path = Path(tmpdir / company.public_key / '123.jpg')
    assert path.exists()
    with Image.open(str(path)) as im:
        assert im.size == (1000, 1000)
        assert im.getpixel((1, 1)) == (50, 100, 149)  # image has been rotated
    path = Path(tmpdir / company.public_key / '123.thumb.jpg')
    assert path.exists()
    with Image.open(str(path)) as im:
        assert im.size == (256, 256)
        assert im.getpixel((1, 1)) == (50, 100, 149)
async def test_real_s3_test(cli, db_conn, company, image_download_url, tmpdir,
                            worker, settings):
    r = await signed_request(cli,
                             f'/{company.public_key}/webhook/contractor',
                             id=123,
                             first_name='Fred')
    assert r.status == 201, await r.text()
    await worker.run_check()

    cons = sorted([
        (cs.first_name, cs.photo_hash)
        async for cs in await db_conn.execute(sa_contractors.select())
    ])
    assert cons == [('Fred', '-')]

    r = await signed_request(
        cli,
        f'/{company.public_key}/webhook/contractor',
        id=124,
        first_name='George',
        photo=f'{image_download_url}?format=JPEG',
    )
    assert r.status == 201, await r.text()
    await worker.run_check()

    # Checking URL is accessible
    r = requests.get(f'{settings.images_url}/{company.public_key}/124.jpg')
    assert r.status_code == 200
    s3_client = boto3.Session(aws_access_key_id=settings.aws_access_key,
                              aws_secret_access_key=settings.aws_secret_key)
    bucket = s3_client.resource('s3').Bucket(settings.aws_bucket_name)
    r = bucket.objects.filter(Prefix=f'{company.public_key}/').delete()
    assert len(r[0].get('Deleted')) == 2
async def test_create_with_keys_update_contractors_false(cli, db_conn, worker):
    data = {
        'name': 'foobar',
        'public_key': 'x' * 20,
        'private_key': 'y' * 40,
        '_request_time': int(time()),
        'update_contractors': False,
    }
    payload = json.dumps(data)
    b_payload = payload.encode()
    m = hmac.new(b'this is the master key', b_payload, hashlib.sha256)

    headers = {
        'Webhook-Signature': m.hexdigest(),
        'Content-Type': 'application/json',
    }
    r = await cli.post('/companies/create', data=payload, headers=headers)
    assert r.status == 201
    curr = await db_conn.execute(sa_companies.select())
    result = await curr.first()
    assert result.name == 'foobar'
    await worker.run_check()
    assert {(cs.id, cs.first_name, cs.last_name)
            async for cs in await db_conn.execute(sa_contractors.select())
            } == set()
async def test_extra_attributes_special(cli, db_conn, company):
    eas = [
        {
            'machine_name': 'tag_line_a',
            'type': 'checkbox',
            'name': 'Should be missed',
            'value': True,
            'sort_index': 0
        },
        {
            'machine_name': 'whatever',
            'type': 'text_short',
            'name': 'Should be missed',
            'value': 'whatever',
            'sort_index': 0,
        },
        {
            'machine_name': 'tag_line',
            'type': 'text_short',
            'name': 'Should be used',
            'value': 'this is the tag line',
            'sort_index': 10,
        },
        {
            'machine_name': 'foobar',
            'type': 'text_extended',
            'name': 'Primary Description',
            'value': 'Should be used as primary description',
            'sort_index': 1,
        },
        {
            'machine_name': 'no_primary',
            'type': 'text_extended',
            'name': 'Not Primary Description',
            'value':
            'Should not be used as primary description because it has a higher sort index than above',
            'sort_index': 2,
        },
    ]
    r = await signed_request(cli,
                             f'/{company.public_key}/webhook/contractor',
                             id=123,
                             deleted=False,
                             first_name='Fred',
                             extra_attributes=eas)
    assert r.status == 201, await r.text()
    curr = await db_conn.execute(sa_contractors.select())
    result = await curr.first()
    assert result.id == 123
    assert result.first_name == 'Fred'
    assert result.tag_line == 'this is the tag line'
    assert result.primary_description == 'Should be used as primary description'
    assert [ea['machine_name'] for ea in result.extra_attributes
            ] == ['tag_line_a', 'whatever', 'no_primary']
async def test_extra_attributes(cli, db_conn, company):
    eas = [
        {
            'machine_name': 'terms',
            'type': 'checkbox',
            'name': 'Terms and Conditions agreement',
            'value': True,
            'sort_index': 0,
        },
        {
            'machine_name': 'bio',
            'type': 'integer',
            'name': 'Teaching Experience',
            'value': 123,
            'sort_index': 0.123
        },
        {
            'machine_name': 'date',
            'type': 'date',
            'name': 'The Date',
            'value': '2032-06-01',
            'sort_index': 0.123
        },
    ]
    r = await signed_request(cli,
                             f'/{company.public_key}/webhook/contractor',
                             id=123,
                             deleted=False,
                             first_name='Fred',
                             extra_attributes=eas)
    assert r.status == 201, await r.text()
    curr = await db_conn.execute(sa_contractors.select())
    result = await curr.first()
    assert result.id == 123
    assert result.first_name == 'Fred'
    assert result.extra_attributes == [{
        k: v
        for k, v in ea_.items() if k != 'sort_index'
    } for ea_ in eas]
    assert result.tag_line is None
    assert result.primary_description is None

    r = await cli.get(cli.server.app.router['contractor-get'].url_for(
        company='thepublickey', id='123', slug='x'))
    assert r.status == 200, await r.text()
    obj = await r.json()
    assert obj['id'] == 123
    assert len(obj['extra_attributes']) == 3
    assert obj['extra_attributes'][2]['value'] == '2032-06-01'
async def test_update(cli, db_conn, company):
    assert [
        cs.first_name
        async for cs in await db_conn.execute(sa_contractors.select())
    ] == []
    r = await signed_request(cli,
                             f'/{company.public_key}/webhook/contractor',
                             id=123,
                             first_name='Fred')
    assert r.status == 201
    assert [
        cs.first_name
        async for cs in await db_conn.execute(sa_contractors.select())
    ] == ['Fred']

    r = await signed_request(cli,
                             f'/{company.public_key}/webhook/contractor',
                             id=123,
                             first_name='George')
    assert r.status == 200
    assert [
        cs.first_name
        async for cs in await db_conn.execute(sa_contractors.select())
    ] == ['George']
async def test_add_location(cli, db_conn, company):
    r = await signed_request(
        cli,
        f'/{company.public_key}/webhook/contractor',
        signing_key_='this is the master key',
        id=321,
        location=dict(latitude=12.345, longitude=56.789),
    )
    assert r.status == 201, await r.text()
    curr = await db_conn.execute(sa_contractors.select())
    result = await curr.first()
    assert result.id == 321
    assert result.review_rating is None
    assert result.review_duration == 0
    assert result.latitude == 12.345
    assert result.longitude == 56.789
async def test_add_review_info(cli, db_conn, company):
    r = await signed_request(
        cli,
        f'/{company.public_key}/webhook/contractor',
        signing_key_='this is the master key',
        id=321,
        review_rating=3.5,
        review_duration=7200,
    )
    assert r.status == 201, await r.text()
    curr = await db_conn.execute(sa_contractors.select())
    result = await curr.first()
    assert result.id == 321
    assert result.review_rating == 3.5
    assert result.review_duration == 7200
    assert result.latitude is None
    assert result.longitude is None
async def test_create_company_key(cli, db_conn, company):
    r = await signed_request(
        cli,
        f'/{company.public_key}/webhook/contractor',
        signing_key_=company.private_key,
        id=123,
        deleted=False,
        first_name='Fred',
        last_name='Bloggs',
    )
    assert r.status == 201, await r.text()
    response_data = await r.json()
    assert response_data == {
        'details': 'contractor created',
        'status': 'success'
    }
    curr = await db_conn.execute(sa_contractors.select())
    result = await curr.first()
    assert result.id == 123
    assert result.first_name == 'Fred'
    assert result.extra_attributes == []
async def test_shorten_tag_line(cli, db_conn, company):
    eas = [
        {
            'machine_name': 'whatever',
            'type': 'text_short',
            'name': 'Should be tag line?',
            'value': 'Should be tag line.' * 50,
            'sort_index': 0,
        },
    ]
    r = await signed_request(cli,
                             f'/{company.public_key}/webhook/contractor',
                             id=123,
                             deleted=False,
                             first_name='Fred',
                             extra_attributes=eas)
    assert r.status == 201, await r.text()
    curr = await db_conn.execute(sa_contractors.select())
    result = await curr.first()
    assert result.id == 123
    assert result.first_name == 'Fred'
    assert len(result.tag_line) == 255
async def test_extra_attributes_null(cli, db_conn, company):
    eas = [{
        'machine_name': 'terms',
        'type': 'checkbox',
        'name': 'Terms and Conditions agreement',
        'value': None,
        'id': 381,
        'sort_index': 0,
    }]
    r = await signed_request(cli,
                             f'/{company.public_key}/webhook/contractor',
                             id=123,
                             deleted=False,
                             first_name='Fred',
                             extra_attributes=eas)
    assert r.status == 201, await r.text()
    curr = await db_conn.execute(sa_contractors.select())
    result = await curr.first()
    assert result.id == 123
    assert result.first_name == 'Fred'
    assert result.extra_attributes == []
    assert result.tag_line is None
    assert result.primary_description is None
async def test_mass_contractor_create(cli, db_conn, company,
                                      image_download_url, monkeypatch, tmpdir,
                                      worker):
    monkeypatch.setattr(boto3, 'client', fake_s3_client(tmpdir))

    data = {'contractors': []}
    eas = [
        {
            'machine_name': 'terms',
            'type': 'checkbox',
            'name': 'Terms and Conditions agreement',
            'value': True,
            'sort_index': 0,
        },
        {
            'machine_name': 'bio',
            'type': 'integer',
            'name': 'Teaching Experience',
            'value': 123,
            'sort_index': 0.123
        },
        {
            'machine_name': 'date',
            'type': 'date',
            'name': 'The Date',
            'value': '2032-06-01',
            'sort_index': 0.123
        },
    ]
    for i in range(1, 3):
        data['contractors'].append(
            dict(
                id=123 * i,
                first_name='Fred',
                skills=[
                    {
                        'subject_id': 1,
                        'qual_level_id': 1,
                        'qual_level': 'GCSE',
                        'subject': 'Algebra',
                        'qual_level_ranking': 16.0,
                        'category': 'Maths',
                    },
                    {
                        'subject_id': 2,
                        'qual_level_id': 1,
                        'qual_level': 'GCSE',
                        'subject': 'Language',
                        'qual_level_ranking': 16.0,
                        'category': 'English',
                    },
                ],
                location=dict(latitude=12.345, longitude=56.789),
                review_rating=3.5,
                review_duration=7200,
                labels=[{
                    'machine_name': 'foobar',
                    'name': 'Foobar'
                }],
                photo=f'{image_download_url}?format=JPEG',
                extra_attributes=eas,
            ))
    r = await signed_request(cli,
                             f'/{company.public_key}/webhook/contractor/mass',
                             signing_key_='this is the master key',
                             **data)
    assert r.status == 200
    assert {'status': 'success'} == await r.json()
    assert 2 == await count(db_conn, sa_contractors)
    await worker.run_check()

    curr = await db_conn.execute(sa_contractors.select())
    all_cons = await curr.fetchall()
    assert all(con_id in tuple(c.id for c in all_cons)
               for con_id in (123, 246))

    curr = await db_conn.execute(
        sa_contractors.select().where(sa_contractors.c.id == 123))
    result = await curr.first()
    assert result.id == 123
    assert result.first_name == 'Fred'
    assert not result.last_name

    curr = await db_conn.execute(
        sa_contractors.select().where(sa_contractors.c.id == 246))
    result = await curr.first()
    assert result.id == 246
    assert result.first_name == 'Fred'
    assert not result.last_name

    for con in data['contractors']:
        con['last_name'] = 'Bob'
    data['contractors'].append(
        dict(
            id=123 * 3,
            first_name='Jim',
            last_name='Bell',
            skills=[
                {
                    'subject_id': 1,
                    'qual_level_id': 1,
                    'qual_level': 'GCSE',
                    'subject': 'Algebra',
                    'qual_level_ranking': 16.0,
                    'category': 'Maths',
                },
                {
                    'subject_id': 2,
                    'qual_level_id': 1,
                    'qual_level': 'GCSE',
                    'subject': 'Language',
                    'qual_level_ranking': 16.0,
                    'category': 'English',
                },
            ],
            location=dict(latitude=12.345, longitude=56.789),
            review_rating=3.5,
            review_duration=7200,
            labels=[{
                'machine_name': 'foobar',
                'name': 'Foobar'
            }],
            photo=f'{image_download_url}?format=JPEG',
            extra_attributes=eas,
        ))

    r = await signed_request(cli,
                             f'/{company.public_key}/webhook/contractor/mass',
                             signing_key_='this is the master key',
                             **data)
    assert r.status == 200, await r.text()
    assert {'status': 'success'} == await r.json()
    assert 3 == await count(db_conn, sa_contractors)
    await worker.run_check()

    curr = await db_conn.execute(sa_contractors.select())
    all_cons = await curr.fetchall()
    assert all(con_id in tuple(c.id for c in all_cons)
               for con_id in (123, 246, 369))

    curr = await db_conn.execute(
        sa_contractors.select().where(sa_contractors.c.id == 123))
    result = await curr.first()
    assert result.id == 123
    assert result.first_name == 'Fred'
    assert result.last_name == 'Bob'

    curr = await db_conn.execute(
        sa_contractors.select().where(sa_contractors.c.id == 246))
    result = await curr.first()
    assert result.id == 246
    assert result.first_name == 'Fred'
    assert result.last_name == 'Bob'

    curr = await db_conn.execute(
        sa_contractors.select().where(sa_contractors.c.id == 369))
    result = await curr.first()
    assert result.id == 369
    assert result.first_name == 'Jim'
    assert result.last_name == 'Bell'