def test_pubished_date_failure(database):
    """ Test failure for when provided, AwardeeOrRecipientUEI must be registered (not necessarily active) in SAM,
        unless the ActionDate is before October 1, 2010.
    """

    pub_fabs_1 = PublishedFABSFactory(unique_award_key='active_key',
                                      action_date='20220404',
                                      is_active=True)
    pub_fabs_2 = PublishedFABSFactory(unique_award_key='inactive_key',
                                      action_date='20091001',
                                      is_active=False)
    models = [pub_fabs_1, pub_fabs_2]

    # new records that may or may not be related to older awards
    recipient = SAMRecipient(uei='1111111111111E')
    fabs_1 = FABSFactory(uei='12345',
                         assistance_type='02',
                         action_date='10/02/2010',
                         correction_delete_indicatr='',
                         unique_award_key='active_key')
    fabs_2 = FABSFactory(uei='12345',
                         assistance_type='06',
                         action_date='04/05/2022',
                         correction_delete_indicatr=None,
                         unique_award_key='inactive_key')
    models += [recipient, fabs_1, fabs_2]

    errors = number_of_errors(_FILE, database, models=models)
    assert errors == 2
示例#2
0
def test_failure(database):
    """ The unique combination of FAIN, AwardModificationAmendmentNumber, URI, CFDA_Number, and
        AwardingSubTierAgencyCode must exist as a currently published record when the record is a deletion (i.e., if
        CorrectionDeleteIndicator = D). Ignore all other CorrectionDeleteIndicators in this rule.
    """

    fabs_1 = FABSFactory(afa_generated_unique='ama1asta1fain2uri1',
                         correction_delete_indicatr='D')
    # Test that capitalization differences don't affect the error
    fabs_2 = FABSFactory(afa_generated_unique='amA1astA1fain2uRi1',
                         correction_delete_indicatr='D')

    pub_fabs_1 = PublishedFABSFactory(
        afa_generated_unique='ama1asta1fain1uri1',
        correction_delete_indicatr=None,
        is_active=True)
    pub_fabs_2 = PublishedFABSFactory(
        afa_generated_unique='ama1asta1fAin2uri1',
        correction_delete_indicatr=None,
        is_active=False)

    errors = number_of_errors(_FILE,
                              database,
                              models=[fabs_1, fabs_2, pub_fabs_1, pub_fabs_2])
    assert errors == 2
def test_success(database):
    """ ActionType should be B, C, or D for transactions that modify existing awards.
        For aggregate (RecordType = 1) record transactions, we consider a record a modification if its combination of
        URI + AwardingSubTierAgencyCode matches an existing published FABS record of the same RecordType.
        For non-aggregate (RecordType = 2 or 3) transactions, we consider a record a modification if its combination
        of FAIN + AwardingSubTierCode matches those of an existing published non-aggregate FABS record
        (RecordType = 2 or 3) of the same RecordType.
    """
    fabs_1 = FABSFactory(unique_award_key='unique1', action_type='B', correction_delete_indicatr=None)
    fabs_2 = FABSFactory(unique_award_key='unique2', action_type='d', correction_delete_indicatr='')

    # Ignore delete/correction record
    fabs_3 = FABSFactory(unique_award_key='unique2', action_type='A', correction_delete_indicatr='D')
    fabs_4 = FABSFactory(unique_award_key='unique2', action_type='A', correction_delete_indicatr='c')

    # This is an inactive award so it will be ignored
    fabs_5 = FABSFactory(unique_award_key='unique3', action_type='E', correction_delete_indicatr=None)

    # This record doesn't have a matching published award at all
    fabs_6 = FABSFactory(unique_award_key='unique4', action_type='A', correction_delete_indicatr='')

    pub_fabs_1 = PublishedFABSFactory(unique_award_key='unique3', is_active=False)
    pub_fabs_2 = PublishedFABSFactory(unique_award_key='unique1', is_active=True)
    pub_fabs_3 = PublishedFABSFactory(unique_award_key='unique2', is_active=True)

    errors = number_of_errors(_FILE, database, models=[fabs_1, fabs_2, fabs_3, fabs_4, fabs_5, fabs_6, pub_fabs_1,
                                                       pub_fabs_2, pub_fabs_3])
    assert errors == 0
示例#4
0
def test_success(database):
    """ ActionType should be "A" for the initial transaction of a new, non-aggregate award (RecordType = 2 or 3) and
        “A” or “E” for a new aggregate award (RecordType = 1). An aggregate record transaction is considered the
        initial transaction of a new award if it contains a unique combination of URI + AwardingSubTierAgencyCode when
        compared to currently published FABS records of the same RecordType. A non-aggregate (RecordType = 2 or 3)
        transaction is considered the initial transaction of a new award if it contains a unique combination of FAIN +
        AwardingSubTierAgencyCode when compared to currently published non-aggregate FABS records (RecordType = 2 or 3)
        of the same RecordType.
    """
    fabs_1 = FABSFactory(unique_award_key='unique1',
                         action_type='A',
                         record_type=1,
                         correction_delete_indicatr=None)
    fabs_2 = FABSFactory(unique_award_key='unique1',
                         action_type='e',
                         record_type=1,
                         correction_delete_indicatr='')
    fabs_3 = FABSFactory(unique_award_key='unique2',
                         action_type='a',
                         record_type=3,
                         correction_delete_indicatr='')

    # Ignore delete/correction record
    fabs_4 = FABSFactory(unique_award_key='unique1',
                         action_type='C',
                         record_type=3,
                         correction_delete_indicatr='D')
    fabs_5 = FABSFactory(unique_award_key='unique1',
                         action_type='C',
                         record_type=3,
                         correction_delete_indicatr='c')

    # This is an active award so it will be ignored
    fabs_6 = FABSFactory(unique_award_key='unique3',
                         action_type='d',
                         record_type=2,
                         correction_delete_indicatr=None)
    fabs_7 = FABSFactory(unique_award_key='unique3',
                         action_type='e',
                         record_type=2,
                         correction_delete_indicatr=None)

    pub_fabs_1 = PublishedFABSFactory(unique_award_key='unique2',
                                      is_active=False)
    pub_fabs_2 = PublishedFABSFactory(unique_award_key='unique3',
                                      is_active=True)

    errors = number_of_errors(_FILE,
                              database,
                              models=[
                                  fabs_1, fabs_2, fabs_3, fabs_4, fabs_5,
                                  fabs_6, fabs_7, pub_fabs_1, pub_fabs_2
                              ])
    assert errors == 0
示例#5
0
def test_failure(database):
    """ Test failure for AwardeeOrRecipientUEI is required where ActionDate is after October 1, 2010, unless the record
        is an aggregate or PII-redacted non-aggregate record (RecordType = 1 or 3) or the recipient is an individual
        (BusinessTypes includes 'P').
    """
    # Note: for FABS 31.2.1, we're setting assistance types to NOT 06, 07, 08, 09, 10, or 11 or having the base
    #       actiondate NOT be less than April 4, 2022. This rule will not trigger if those *don't* apply.
    #       FABS 31.2.2 *will* trigger when these apply.

    pub_fabs_1 = PublishedFABSFactory(unique_award_key='before_key',
                                      action_date='20091001',
                                      is_active=True)
    pub_fabs_2 = PublishedFABSFactory(unique_award_key='after_key',
                                      action_date='20230404',
                                      is_active=True)
    pub_fabs_3 = PublishedFABSFactory(unique_award_key='inactive_key',
                                      action_date='20091001',
                                      is_active=False)
    models = [pub_fabs_1, pub_fabs_2, pub_fabs_3]

    fabs_1 = FABSFactory(record_type=2,
                         business_types='AbC',
                         uei=None,
                         action_date='10/02/2010',
                         assistance_type='02',
                         correction_delete_indicatr='',
                         unique_award_key='before_key')
    fabs_2 = FABSFactory(record_type=5,
                         business_types='aBc',
                         uei=None,
                         action_date='10/02/2010',
                         assistance_type='06',
                         correction_delete_indicatr='C',
                         unique_award_key='after_key')
    fabs_3 = FABSFactory(record_type=4,
                         business_types='AbC',
                         uei='',
                         action_date='04/05/2023',
                         assistance_type='07',
                         correction_delete_indicatr='c',
                         unique_award_key='new_key')
    fabs_4 = FABSFactory(record_type=5,
                         business_types='aBc',
                         uei='',
                         action_date='04/05/2023',
                         assistance_type='08',
                         correction_delete_indicatr=None,
                         unique_award_key='inactive_key')
    models += [fabs_1, fabs_2, fabs_3, fabs_4]

    errors = number_of_errors(_FILE, database, models=models)
    assert errors == 4
示例#6
0
def test_failure(database):
    """ Fail ActionType should be "A" for the initial transaction of a new, non-aggregate award (RecordType = 2 or 3)
    and “A” or “E” for a new aggregate award (RecordType = 1). An aggregate record transaction is considered the initial
    transaction of a new award if it contains a unique combination of URI + AwardingSubTierAgencyCode when compared to
    currently published FABS records of the same RecordType. A non-aggregate (RecordType = 2 or 3) transaction is
    considered the initial transaction of a new award if it contains a unique combination of FAIN +
    AwardingSubTierAgencyCode when compared to currently published non-aggregate FABS records (RecordType = 2 or 3) of
    the same RecordType.
    """

    fabs_1 = FABSFactory(unique_award_key='unique1',
                         action_type='b',
                         record_type=1,
                         correction_delete_indicatr=None)

    # E is only valid for record type 1
    fabs_2 = FABSFactory(unique_award_key='unique2',
                         action_type='E',
                         record_type=2,
                         correction_delete_indicatr='')

    pub_fabs_1 = PublishedFABSFactory(unique_award_key='unique2',
                                      is_active=False)

    errors = number_of_errors(_FILE,
                              database,
                              models=[fabs_1, fabs_2, pub_fabs_1])
    assert errors == 2
def test_pubished_date_success(database):
    """ Test success for when provided, AwardeeOrRecipientUEI must be registered (not necessarily active) in SAM,
        unless the ActionDate is before October 1, 2010.
    """

    pub_fabs_1 = PublishedFABSFactory(unique_award_key='active_key',
                                      action_date='20091001',
                                      is_active=True)
    pub_fabs_2 = PublishedFABSFactory(unique_award_key='inactive_key',
                                      action_date='20091001',
                                      is_active=False)
    models = [pub_fabs_1, pub_fabs_2]

    # new records that may or may not be related to older awards
    recipient = SAMRecipient(uei='22222222222E')
    fabs_1 = FABSFactory(uei=recipient.uei,
                         assistance_type='02',
                         action_date='10/02/2010',
                         correction_delete_indicatr='',
                         unique_award_key='active_key')
    fabs_2 = FABSFactory(uei=recipient.uei.lower(),
                         assistance_type='02',
                         action_date='10/02/2010',
                         correction_delete_indicatr='c',
                         unique_award_key='active_key')
    fabs_3 = FABSFactory(uei=None,
                         assistance_type='01',
                         action_date='10/02/2010',
                         correction_delete_indicatr='c',
                         unique_award_key='active_key')
    # Before October 1, 2010
    fabs_4 = FABSFactory(uei='12345',
                         assistance_type='02',
                         action_date='09/30/2010',
                         correction_delete_indicatr='C',
                         unique_award_key='new_key')
    # Ignore correction delete indicator of D
    fabs_5 = FABSFactory(uei='12345',
                         assistance_type='01',
                         action_date='10/02/2010',
                         correction_delete_indicatr='d',
                         unique_award_key='inactive_key')

    models += [recipient, fabs_1, fabs_2, fabs_3, fabs_4, fabs_5]

    errors = number_of_errors(_FILE, database, models=models)
    assert errors == 0
def test_success(database):
    """ The unique combination of FAIN, AwardModificationAmendmentNumber, URI, CFDA_Number, and
        AwardingSubTierAgencyCode must exist as a currently published record when the record is a correction (i.e., if
        CorrectionDeleteIndicator = C). Ignore all other CorrectionDeleteIndicators in this rule.
    """
    fabs_1 = FABSFactory(afa_generated_unique='ama1asta1fain1uri1', correction_delete_indicatr=None)
    fabs_2 = FABSFactory(afa_generated_unique='ama1asta1fain2uri1', correction_delete_indicatr='C')
    fabs_3 = FABSFactory(afa_generated_unique='ama2asta1fain1uri1', correction_delete_indicatr='D')

    pub_fabs_1 = PublishedFABSFactory(afa_generated_unique='ama1asta1fain2uri1', correction_delete_indicatr=None,
                                      is_active=True)
    pub_fabs_2 = PublishedFABSFactory(afa_generated_unique='ama1asta1fain1uri1', correction_delete_indicatr=None,
                                      is_active=True)
    pub_fabs_3 = PublishedFABSFactory(afa_generated_unique='ama2asta1fain1uri1', correction_delete_indicatr=None,
                                      is_active=False)

    errors = number_of_errors(_FILE, database, models=[fabs_1, fabs_2, fabs_3, pub_fabs_1, pub_fabs_2, pub_fabs_3])
    assert errors == 0
示例#9
0
def test_failure(database):
    """ Test fail that empty funding office codes aren't matching invalid office codes from the base record. """

    office_1 = OfficeFactory(office_code='12345a', contract_funding_office=False,
                             financial_assistance_funding_office=True)
    office_2 = OfficeFactory(office_code='abcd', contract_funding_office=True,
                             financial_assistance_funding_office=False)
    # Invalid code in record
    pub_fabs_1 = PublishedFABSFactory(funding_office_code='abc', unique_award_key='zyxwv_123', action_date='20181018',
                                      award_modification_amendme='0', is_active=True)
    # Earliest record inactive, newer record has invalid entry
    pub_fabs_2 = PublishedFABSFactory(funding_office_code='12345a', unique_award_key='4321_cba', action_date='20181018',
                                      award_modification_amendme='0', is_active=False)
    pub_fabs_3 = PublishedFABSFactory(funding_office_code='abc', unique_award_key='4321_cba', action_date='20181019',
                                      award_modification_amendme='1', is_active=True)
    # Has a valid code but it's not a funding assistance office
    pub_fabs_4 = PublishedFABSFactory(funding_office_code='abcd', unique_award_key='123_abc', action_date='20181018',
                                      award_modification_amendme='0', is_active=True)
    # award_modification_amendme number is null
    pub_fabs_5 = PublishedFABSFactory(funding_office_code='abc', unique_award_key='zyxwv_1234', action_date='20181018',
                                      award_modification_amendme=None, is_active=True)

    # Entry for invalid code in base record
    fabs_1 = FABSFactory(funding_office_code='', unique_award_key='zyxwv_123', action_date='20181020',
                         award_modification_amendme='2', correction_delete_indicatr=None)
    # Entry with award_modification_amendme null
    fabs_2 = FABSFactory(funding_office_code='', unique_award_key='zyxwv_123', action_date='20181020',
                         award_modification_amendme=None, correction_delete_indicatr=None)
    # New entry for earliest inactive
    fabs_3 = FABSFactory(funding_office_code='', unique_award_key='4321_cba', action_date='20181020',
                         award_modification_amendme='2', correction_delete_indicatr=None)
    # New entry for has valid non-funding assistance code
    fabs_4 = FABSFactory(funding_office_code='', unique_award_key='123_abc', action_date='20181020',
                         award_modification_amendme='2', correction_delete_indicatr=None)
    # Entry for award_modification_amendme null in base record
    fabs_5 = FABSFactory(funding_office_code='', unique_award_key='zyxwv_1234', action_date='20181020',
                         award_modification_amendme='2', correction_delete_indicatr=None)
    errors = number_of_errors(_FILE, database, models=[office_1, office_2, pub_fabs_1, pub_fabs_2, pub_fabs_3,
                                                       pub_fabs_4, pub_fabs_5, fabs_1, fabs_2, fabs_3, fabs_4, fabs_5])
    assert errors == 5
def test_failure(database):
    """ Fail ActionType should be B, C, or D for transactions that modify existing awards. For aggregate
        (RecordType = 1) record transactions, we consider a record a modification if its combination of URI +
        AwardingSubTierAgencyCode matches an existing published FABS record of the same RecordType.
        For non-aggregate (RecordType = 2 or 3) transactions, we consider a record a modification if its combination
        of FAIN + AwardingSubTierCode matches those of an existing published non-aggregate FABS record
        (RecordType = 2 or 3) of the same RecordType.
    """

    fabs_1 = FABSFactory(unique_award_key='unique1', action_type='a', correction_delete_indicatr=None)
    fabs_2 = FABSFactory(unique_award_key='unique1', action_type='E', correction_delete_indicatr='')

    pub_fabs_1 = PublishedFABSFactory(unique_award_key='unique1', is_active=True)

    errors = number_of_errors(_FILE, database, models=[fabs_1, fabs_2, pub_fabs_1])
    assert errors == 2
示例#11
0
def test_failure(database):
    """ The combination of FAIN, AwardModificationAmendmentNumber, URI, CFDA_Number, and AwardingSubTierAgencyCode must
        be unique from currently published ones unless the record is a correction or deletion
        (i.e., if CorrectionDeleteIndicator = C or D).
    """

    fabs_1 = FABSFactory(afa_generated_unique='ama1asta1fain1uri1',
                         correction_delete_indicatr=None)
    # Test that capitalization differences don't affect the error
    fabs_2 = FABSFactory(afa_generated_unique='amA1asta1faiN1uri1',
                         correction_delete_indicatr=None)
    pub_fabs_1 = PublishedFABSFactory(
        afa_generated_unique='ama1asTa1fain1uri1',
        correction_delete_indicatr=None,
        is_active=True)

    errors = number_of_errors(_FILE,
                              database,
                              models=[fabs_1, fabs_2, pub_fabs_1])
    assert errors == 2
示例#12
0
def test_success_ignore_null_pub_fabs(database):
    """ Test that empty funding office codes aren't matching invalid office codes from the base record. """

    office_1 = OfficeFactory(office_code='12345b', contract_funding_office=False,
                             financial_assistance_funding_office=True)
    # Base record has no funding office code, future records don't affect it
    pub_fabs_1 = PublishedFABSFactory(funding_office_code='', unique_award_key='zyxwv_123', action_date='20181018',
                                      award_modification_amendme='0', is_active=True)
    pub_fabs_2 = PublishedFABSFactory(funding_office_code='abc', unique_award_key='zyxwv_123', action_date='20181019',
                                      award_modification_amendme='1', is_active=True)
    # Base record has an invalid code but new record has a funding office entered (ignore this rule)
    pub_fabs_3 = PublishedFABSFactory(funding_office_code='abc', unique_award_key='abcd_123', action_date='20181019',
                                      award_modification_amendme='0', is_active=True)
    # Base record with a valid office code (case insensitive)
    pub_fabs_4 = PublishedFABSFactory(funding_office_code='12345B', unique_award_key='1234_abc', action_date='20181019',
                                      award_modification_amendme='0', is_active=True)
    # Earliest record inactive, newer record has valid entry, inactive date matching active doesn't mess it up
    pub_fabs_5 = PublishedFABSFactory(funding_office_code='abc', unique_award_key='4321_cba', action_date='20181018',
                                      award_modification_amendme='0', is_active=False)
    pub_fabs_6 = PublishedFABSFactory(funding_office_code='abc', unique_award_key='4321_cba', action_date='20181019',
                                      award_modification_amendme='1', is_active=False)
    pub_fabs_7 = PublishedFABSFactory(funding_office_code='12345b', unique_award_key='4321_cba', action_date='20181019',
                                      award_modification_amendme='1', is_active=True)

    # New entry for base award with no office code
    fabs_1 = FABSFactory(funding_office_code='', unique_award_key='zyxwv_123', action_date='20181020',
                         award_modification_amendme='2', correction_delete_indicatr=None)
    # New entry for base award with invalid code but entry has a funding office code
    fabs_2 = FABSFactory(funding_office_code='abd', unique_award_key='abcd_123', action_date='20181020',
                         award_modification_amendme='1', correction_delete_indicatr=None)
    # New entry for valid funding office
    fabs_3 = FABSFactory(funding_office_code=None, unique_award_key='1234_abc', action_date='20181020',
                         award_modification_amendme='1', correction_delete_indicatr=None)
    # Correction to base record (ignore)
    fabs_4 = FABSFactory(funding_office_code='', unique_award_key='abcd_123', action_date='20181019',
                         award_modification_amendme='0', correction_delete_indicatr='C')
    # New entry for earliest inactive
    fabs_5 = FABSFactory(funding_office_code='', unique_award_key='4321_cba', action_date='20181020',
                         award_modification_amendme='2', correction_delete_indicatr=None)
    errors = number_of_errors(_FILE, database, models=[office_1, pub_fabs_1, pub_fabs_2, pub_fabs_3, pub_fabs_4,
                                                       pub_fabs_5, pub_fabs_6, pub_fabs_7, fabs_1, fabs_2, fabs_3,
                                                       fabs_4, fabs_5])
    assert errors == 0
示例#13
0
def test_success(database):
    """ Test success for AwardeeOrRecipientUEI is required where ActionDate is after October 1, 2010, unless the record
        is an aggregate or PII-redacted non-aggregate record (RecordType = 1 or 3) or the recipient is an individual
        (BusinessTypes includes 'P').
    """
    # Note: for FABS 31.2.1, we're setting assistance types to NOT 06, 07, 08, 09, 10, or 11 or having the base
    #       actiondate NOT be less than April 4, 2023. This rule will not trigger if those *don't* apply.
    #       FABS 31.2.2 *will* trigger when these apply.

    pub_fabs_1 = PublishedFABSFactory(unique_award_key='before_key',
                                      action_date='20091001',
                                      is_active=True)
    pub_fabs_2 = PublishedFABSFactory(unique_award_key='after_key',
                                      action_date='20230404',
                                      is_active=True)
    pub_fabs_3 = PublishedFABSFactory(unique_award_key='inactive_key',
                                      action_date='20091001',
                                      is_active=False)
    models = [pub_fabs_1, pub_fabs_2, pub_fabs_3]

    # new records that may or may not be related to older awards
    fabs_1 = FABSFactory(record_type=2,
                         business_types='AbC',
                         uei='test',
                         action_date='10/02/2010',
                         assistance_type='03',
                         correction_delete_indicatr='',
                         unique_award_key='after_key')
    fabs_2 = FABSFactory(record_type=5,
                         business_types='aBc',
                         uei='test',
                         action_date='10/02/2010',
                         assistance_type='02',
                         correction_delete_indicatr='c',
                         unique_award_key='after_key')
    # Ignored for dates before Oct 1 2010
    fabs_3 = FABSFactory(record_type=2,
                         business_types='AbC',
                         uei=None,
                         action_date='09/01/2010',
                         assistance_type='02',
                         correction_delete_indicatr='',
                         unique_award_key='after_key')
    # Ignored for record type 1/3
    fabs_4 = FABSFactory(record_type=1,
                         business_types='AbC',
                         uei=None,
                         action_date='10/01/2010',
                         assistance_type='03',
                         correction_delete_indicatr='',
                         unique_award_key='after_key')
    # Ignored for listed assistance types
    fabs_5 = FABSFactory(record_type=1,
                         business_types='AbC',
                         uei='',
                         action_date='09/01/2010',
                         assistance_type='06',
                         correction_delete_indicatr='',
                         unique_award_key='after_key')
    # Ignored when business types include P
    fabs_6 = FABSFactory(record_type=5,
                         business_types='aBp',
                         uei=None,
                         action_date='10/02/2010',
                         assistance_type='05',
                         correction_delete_indicatr='',
                         unique_award_key='after_key')
    # Ignore correction delete indicator of D
    fabs_7 = FABSFactory(record_type=2,
                         business_types='AbC',
                         uei=None,
                         action_date='10/02/2010',
                         assistance_type='08',
                         correction_delete_indicatr='d',
                         unique_award_key='new_key')
    # Ensuring that this rule gets ignored when the base actiondate case does apply
    fabs_8 = FABSFactory(record_type=2,
                         business_types='AbC',
                         uei=None,
                         action_date='10/02/2010',
                         assistance_type='06',
                         correction_delete_indicatr='',
                         unique_award_key='before_key')
    fabs_9 = FABSFactory(record_type=2,
                         business_types='AbC',
                         uei='',
                         action_date='10/02/2010',
                         assistance_type='06',
                         correction_delete_indicatr='',
                         unique_award_key='new_key')
    fabs_10 = FABSFactory(record_type=2,
                          business_types='AbC',
                          uei='',
                          action_date='10/02/2010',
                          assistance_type='07',
                          correction_delete_indicatr='',
                          unique_award_key='inactive_key')

    models += [
        fabs_1, fabs_2, fabs_3, fabs_4, fabs_5, fabs_6, fabs_7, fabs_8, fabs_9,
        fabs_10
    ]
    errors = number_of_errors(_FILE, database, models=models)
    assert errors == 0
def test_fix_broken_links(database, monkeypatch):
    """ Ensure that fix_broken_links works as intended """

    # Setup - create awards, procurements/grants, subawards
    sess = database.session
    sess.query(Subaward).delete(synchronize_session=False)
    sess.commit()

    parent_recipient, recipient, dom_country, int_country = reference_data(sess)
    min_date = '2019-06-06'
    award_updated_at = '2019-06-07'

    # Setup - Grants
    sub = SubmissionFactory(submission_id=1)
    sub2 = SubmissionFactory(submission_id=2)
    sub3 = SubmissionFactory(submission_id=3)
    fabs_non_pop_subtier = PublishedFABSFactory(
        submission_id=sub.submission_id,
        awarding_sub_tier_agency_c='1234',
        record_type=2,
        unique_award_key='NON-POP-SUB',
        fain='NON-FAIN-WITH-DASHES-POP-SUB',
        is_active=True,
        updated_at=award_updated_at,
        action_date='2020-01-01'
    )
    fabs_non_pop_subtier_2 = PublishedFABSFactory(
        submission_id=sub.submission_id,
        awarding_sub_tier_agency_c='1234',
        record_type=2,
        unique_award_key='NON-POP-SUB',
        fain='NON-FAIN-WITH-DASHES-POP-SUB',
        is_active=True,
        updated_at=award_updated_at,
        action_date='2020-01-02'
    )
    grant_non_pop_subtier = FSRSGrantFactory(
        fain=fabs_non_pop_subtier.fain.replace('-', ''),
        federal_agency_id='1234',
        awardee_address_country=int_country.country_code,
        principle_place_country=dom_country.country_code,
        parent_uei=parent_recipient.uei,
        cfda_numbers='00.001 CFDA 1; 00.002 CFDA 2',
        obligation_date=datetime.now(),
        date_submitted=datetime(2019, 5, 30, 16, 25, 12, 34)
    )
    sub_grant_non_pop_subtier = FSRSSubgrantFactory(
        parent=grant_non_pop_subtier,
        awardee_address_country=dom_country.country_code,
        principle_place_country=int_country.country_code_2_char,
        parent_uei=parent_recipient.uei,
        uei_number=recipient.uei.lower(),
        subaward_date=datetime.now()
    )
    fabs_non_null_subtier = PublishedFABSFactory(
        submission_id=sub.submission_id,
        awarding_sub_tier_agency_c='5678',
        record_type=2,
        unique_award_key='NON-NULL-SUB',
        fain='NON-FAIN-WITH-DASHES-NULL-SUB',
        is_active=True,
        updated_at=award_updated_at,
        action_date='2020-01-01'
    )

    fabs_non_null_subtier_2 = PublishedFABSFactory(
        submission_id=sub.submission_id,
        awarding_sub_tier_agency_c='5678',
        record_type=2,
        unique_award_key='NON-NULL-SUB',
        fain='NON-FAIN-WITH-DASHES-NULL-SUB',
        is_active=True,
        updated_at=award_updated_at,
        action_date='2020-01-02'
    )
    grant_non_null_subtier = FSRSGrantFactory(
        fain=fabs_non_null_subtier.fain.replace('-', ''),
        federal_agency_id=None,
        awardee_address_country=int_country.country_code_2_char,
        principle_place_country=dom_country.country_code,
        parent_uei=parent_recipient.uei,
        cfda_numbers='00.001 CFDA 1; 00.002 CFDA 2',
        obligation_date=datetime.now(),
        date_submitted=datetime(2019, 5, 30, 16, 25, 12, 34)
    )
    sub_grant_non_null_subtier = FSRSSubgrantFactory(
        parent=grant_non_null_subtier,
        awardee_address_country=dom_country.country_code,
        principle_place_country=int_country.country_code_2_char,
        parent_uei=parent_recipient.uei,
        uei_number=recipient.uei,
        subaward_date=datetime.now()
    )
    fabs_non_other = PublishedFABSFactory(
        submission_id=sub2.submission_id,
        awarding_sub_tier_agency_c='1357',
        record_type=2,
        unique_award_key='NON-OTHER',
        fain='NON-FAIN-WITH-DASHES-OTHER',
        is_active=True,
        updated_at=award_updated_at,
        action_date='2020-01-01'
    )

    fabs_non_other_2 = PublishedFABSFactory(
        submission_id=sub2.submission_id,
        awarding_sub_tier_agency_c='1357',
        record_type=2,
        unique_award_key='NON-OTHER',
        fain='NON-FAIN-WITH-DASHES-OTHER',
        is_active=True,
        updated_at=award_updated_at,
        action_date='2020-01-02'
    )
    fabs_non_other_dup = PublishedFABSFactory(
        submission_id=sub3.submission_id,
        awarding_sub_tier_agency_c='2468',
        record_type=2,
        unique_award_key='NON-OTHER',
        fain='NON-FAIN-WITH-DASHES-OTHER',
        is_active=True,
        updated_at=award_updated_at,
        action_date='2020-01-01'
    )
    fabs_non_other_dup_2 = PublishedFABSFactory(
        submission_id=sub3.submission_id,
        awarding_sub_tier_agency_c='2468',
        record_type=2,
        unique_award_key='NON-OTHER',
        fain='NON-FAIN-WITH-DASHES-OTHER',
        is_active=True,
        updated_at=award_updated_at,
        action_date='2020-01-02'
    )
    grant_non_other = FSRSGrantFactory(
        fain=fabs_non_other.fain.replace('-', ''),
        federal_agency_id=None,
        awardee_address_country=int_country.country_code,
        principle_place_country=dom_country.country_code,
        parent_uei=parent_recipient.uei,
        cfda_numbers='00.001 CFDA 1; 00.002 CFDA 2',
        obligation_date=datetime.now(),
        date_submitted=datetime(2019, 5, 30, 16, 25, 12, 34)
    )
    sub_grant_non_other = FSRSSubgrantFactory(
        parent=grant_non_other,
        awardee_address_country=dom_country.country_code,
        principle_place_country=int_country.country_code_2_char,
        parent_uei=parent_recipient.uei,
        uei_number=recipient.uei,
        subaward_date=datetime.now()
    )
    fabs_agg = PublishedFABSFactory(
        submission_id=sub.submission_id,
        awarding_sub_tier_agency_c='1234',
        record_type=1,
        unique_award_key='AGG',
        fain='AGG-FAIN-WITH-DASHES',
        is_active=True,
        updated_at=award_updated_at,
        action_date='2020-01-01'
    )
    fabs_agg_2 = PublishedFABSFactory(
        submission_id=sub.submission_id,
        awarding_sub_tier_agency_c='1234',
        record_type=1,
        unique_award_key='AGG',
        fain='AGG-FAIN-WITH-DASHES',
        is_active=True,
        updated_at=award_updated_at,
        action_date='2020-01-02'
    )
    grant_agg = FSRSGrantFactory(
        fain=fabs_agg.fain.replace('-', ''),
        federal_agency_id='1234',
        awardee_address_country=int_country.country_code_2_char,
        principle_place_country=dom_country.country_code,
        parent_uei=parent_recipient.uei,
        cfda_numbers='00.003 CFDA 3',
        obligation_date=datetime.now(),
        date_submitted=datetime(2019, 5, 30, 16, 25, 12, 34)
    )
    sub_grant_agg = FSRSSubgrantFactory(
        parent=grant_agg,
        awardee_address_country=dom_country.country_code,
        principle_place_country=int_country.country_code_2_char,
        parent_uei=parent_recipient.uei,
        uei_number=recipient.uei,
        subaward_date=datetime.now()
    )

    # Setup - Contracts
    d1_awd = DetachedAwardProcurementFactory(
        submission_id=sub.submission_id,
        idv_type=None,
        unique_award_key='AWD',
        piid='AWD-PIID-WITH-DASHES',
        parent_award_id='AWD-PARENT-AWARD-ID-WITH-DASHES',
        updated_at=award_updated_at,
        action_date='2020-01-01'
    )
    d1_awd_2 = DetachedAwardProcurementFactory(
        submission_id=sub.submission_id,
        idv_type=None,
        unique_award_key='AWD',
        piid='AWD-PIID-WITH-DASHES',
        parent_award_id='AWD-PARENT-AWARD-ID-WITH-DASHES',
        updated_at=award_updated_at,
        action_date='2020-01-02'
    )
    contract_awd = FSRSProcurementFactory(
        contract_number=d1_awd.piid.replace('-', ''),
        idv_reference_number=d1_awd.parent_award_id.replace('-', ''),
        contracting_office_aid=d1_awd.awarding_sub_tier_agency_c,
        company_address_country=dom_country.country_code,
        principle_place_country=int_country.country_code_2_char,
        uei_number=recipient.uei,
        date_signed=datetime.now(),
        date_submitted=datetime(2019, 5, 30, 16, 25, 12, 34)
    )
    sub_contract_awd = FSRSSubcontractFactory(
        parent=contract_awd,
        company_address_country=int_country.country_code_2_char,
        principle_place_country=dom_country.country_code,
        subcontract_date=datetime.now()
    )
    d1_idv = DetachedAwardProcurementFactory(
        submission_id=sub.submission_id,
        idv_type='C',
        unique_award_key='IDV',
        piid='IDV-PIID-WITH-DASHES',
        parent_award_id='IDV-PARENT-AWARD-IDV-WITH-DASHES',
        updated_at=award_updated_at,
        action_date='2020-01-01'
    )
    d1_idv_2 = DetachedAwardProcurementFactory(
        submission_id=sub.submission_id,
        idv_type='C',
        unique_award_key='IDV',
        piid='IDV-PIID-WITH-DASHES',
        parent_award_id='IDV-PARENT-AWARD-IDV-WITH-DASHES',
        updated_at=award_updated_at,
        action_date='2020-01-02'
    )
    contract_idv = FSRSProcurementFactory(
        contract_number=d1_idv.piid.replace('-', ''),
        idv_reference_number=d1_idv.parent_award_id.replace('-', ''),
        contracting_office_aid=d1_idv.awarding_sub_tier_agency_c,
        company_address_country=dom_country.country_code,
        principle_place_country=int_country.country_code,
        uei_number=recipient.uei,
        date_signed=datetime.now(),
        date_submitted=datetime(2019, 5, 30, 16, 25, 12, 34)
    )
    sub_contract_idv = FSRSSubcontractFactory(
        parent=contract_idv,
        company_address_country=int_country.country_code_2_char,
        principle_place_country=dom_country.country_code,
        subcontract_date=datetime.now()
    )

    # Note: not including d1/fabs data
    sess.add_all([sub, contract_awd, sub_contract_awd, contract_idv, sub_contract_idv])
    sess.add_all([sub, grant_non_pop_subtier, sub_grant_non_pop_subtier, grant_non_null_subtier,
                  sub_grant_non_null_subtier, grant_non_other, sub_grant_non_other, grant_agg, sub_grant_agg])
    sess.commit()

    populate_subaward_table(sess, 'procurement_service', ids=[contract_awd.id, contract_idv.id])
    populate_subaward_table(sess, 'grant_service', ids=[grant_agg.id, grant_non_pop_subtier.id,
                                                        grant_non_null_subtier.id, grant_non_other.id])

    contracts_results = sess.query(Subaward).order_by(Subaward.unique_award_key).\
        filter(Subaward.subaward_type == 'sub-contract').all()
    grants_results = sess.query(Subaward).order_by(Subaward.award_id).\
        filter(Subaward.subaward_type == 'sub-grant').all()
    original_ids = [result.id for result in contracts_results + grants_results]

    grant_created_at = grant_updated_at = grants_results[0].created_at
    contract_created_at = contract_updated_at = contracts_results[0].created_at

    # Expected Results - should be False as the award isn't provided
    assert compare_contract_results(contracts_results[0], d1_awd, contract_awd, sub_contract_awd, dom_country,
                                    int_country, contract_created_at, contract_updated_at) is False
    assert compare_contract_results(contracts_results[1], d1_idv, contract_idv, sub_contract_idv, dom_country,
                                    int_country, contract_created_at, contract_updated_at) is False

    assert compare_grant_results(grants_results[0], fabs_agg, grant_agg, sub_grant_agg, parent_recipient, recipient,
                                 dom_country, int_country, grant_created_at, grant_updated_at) is False
    assert compare_grant_results(grants_results[1], fabs_non_other, grant_non_other, sub_grant_non_other,
                                 parent_recipient, recipient, dom_country, int_country, grant_created_at,
                                 grant_updated_at) is False
    assert compare_grant_results(grants_results[2], fabs_non_null_subtier, grant_non_null_subtier,
                                 sub_grant_non_null_subtier, parent_recipient, recipient, dom_country,
                                 int_country, grant_created_at, grant_updated_at) is False
    assert compare_grant_results(grants_results[3], fabs_non_pop_subtier, grant_non_pop_subtier,
                                 sub_grant_non_pop_subtier, parent_recipient, recipient, dom_country,
                                 int_country, grant_created_at, grant_updated_at) is False

    # now add the awards and fix the broken links
    sess.add_all([d1_awd, d1_awd_2, d1_idv, d1_idv_2, fabs_non_null_subtier, fabs_non_null_subtier_2,
                  fabs_non_pop_subtier, fabs_non_pop_subtier_2, fabs_non_other, fabs_non_other_2, fabs_non_other_dup,
                  fabs_non_other_dup_2, fabs_agg, fabs_agg_2])
    sess.commit()

    updated_proc_count = fix_broken_links(sess, 'procurement_service', min_date=min_date)
    updated_grant_count = fix_broken_links(sess, 'grant_service', min_date=min_date)

    assert updated_proc_count == 2
    # Note: Aggregates and blank federal_agency_ids should still not be linked, so 1 and not 3
    assert updated_grant_count == 1

    contracts_results = sess.query(Subaward).order_by(Subaward.unique_award_key).\
        filter(Subaward.subaward_type == 'sub-contract').all()
    grants_results = sess.query(Subaward).order_by(Subaward.award_id).\
        filter(Subaward.subaward_type == 'sub-grant').all()
    updated_ids = [result.id for result in contracts_results + grants_results]

    contract_created_at = contracts_results[0].created_at
    contract_updated_at = contracts_results[0].updated_at
    grant_created_at = grants_results[0].created_at
    grant_updated_at = grants_results[0].updated_at

    # Expected Results - should now be True as the award is now available
    assert compare_contract_results(contracts_results[0], d1_awd, contract_awd, sub_contract_awd, dom_country,
                                    int_country, contract_created_at, contract_updated_at) is True
    assert compare_contract_results(contracts_results[1], d1_idv, contract_idv, sub_contract_idv, dom_country,
                                    int_country, contract_created_at, contract_updated_at) is True
    assert compare_grant_results(grants_results[0], fabs_agg, grant_agg, sub_grant_agg, parent_recipient, recipient,
                                 dom_country, int_country, grant_created_at, grant_updated_at) is False
    assert compare_grant_results(grants_results[1], fabs_non_null_subtier, grant_non_null_subtier,
                                 sub_grant_non_null_subtier, parent_recipient, recipient, dom_country,
                                 int_country, grant_created_at, grant_updated_at) is False
    assert compare_grant_results(grants_results[2], fabs_non_other, grant_non_other, sub_grant_non_other,
                                 parent_recipient, recipient, dom_country, int_country, grant_created_at,
                                 grant_updated_at) is False
    assert compare_grant_results(grants_results[3], fabs_non_pop_subtier, grant_non_pop_subtier,
                                 sub_grant_non_pop_subtier, parent_recipient, recipient, dom_country,
                                 int_country, grant_created_at, grant_updated_at) is True

    # Ensuring only updates occurred, no deletes/inserts
    assert set(original_ids) == set(updated_ids)
def test_generate_f_file_queries_grants(database, monkeypatch):
    """ generate_f_file_queries should provide queries representing halves of F file data related to a submission
        This will cover grants records.
    """
    # Setup - create awards, procurements/grants, subawards
    sess = database.session
    sess.query(Subaward).delete(synchronize_session=False)
    sess.commit()

    parent_recipient, recipient, dom_country, int_country = reference_data(sess)

    # Setup - create awards, procurements, subcontracts
    sub = SubmissionFactory(submission_id=1)
    # FABS Non-aggregate award with federal_agency_id/awarding_sub_tier_agency_c populated
    fabs_non_pop_subtier = PublishedFABSFactory(
        submission_id=sub.submission_id,
        record_type=2,
        unique_award_key='NON-POP-SUB',
        fain='NON-FAIN-WITH-DASHES-POP-SUB',
        awarding_sub_tier_agency_c='1234',
        is_active=True,
        action_date='2020-01-01'
    )
    fabs_non_pop_subtier_2 = PublishedFABSFactory(
        submission_id=sub.submission_id,
        record_type=2,
        unique_award_key='NON-POP-SUB',
        fain='NON-FAIN-WITH-DASHES-POP-SUB',
        awarding_sub_tier_agency_c='1234',
        is_active=True,
        action_date='2020-01-02'
    )
    grant_non_pop_subtier = FSRSGrantFactory(
        fain=fabs_non_pop_subtier.fain.replace('-', ''),
        federal_agency_id='1234',
        awardee_address_country=int_country.country_code_2_char,
        principle_place_country=dom_country.country_code,
        parent_uei=parent_recipient.uei,
        cfda_numbers='00.001 CFDA 1; 00.002 CFDA 2',
        obligation_date=datetime.now(),
        date_submitted=datetime(2019, 5, 30, 16, 25, 12, 34)
    )
    sub_grant_non_pop_subtier = FSRSSubgrantFactory(
        parent=grant_non_pop_subtier,
        awardee_address_country=dom_country.country_code,
        principle_place_country=int_country.country_code_2_char,
        parent_uei=parent_recipient.uei,
        uei_number=recipient.uei,
        subaward_date=datetime.now()
    )
    # FABS Non-aggregate award with federal_agency_id NULL
    fabs_non_null_sub = PublishedFABSFactory(
        submission_id=sub.submission_id,
        record_type=2,
        unique_award_key='NON-NULL-SUB',
        fain='NON-FAIN-WITH-DASHES-NULL-SUB',
        awarding_sub_tier_agency_c='5678',
        is_active=True,
        action_date='2020-01-01'
    )
    fabs_non_null_sub_2 = PublishedFABSFactory(
        submission_id=sub.submission_id,
        record_type=2,
        unique_award_key='NON-NULL-SUB',
        fain='NON-FAIN-WITH-DASHES-NULL-SUB',
        awarding_sub_tier_agency_c='5678',
        is_active=True,
        action_date='2020-01-02'
    )
    grant_non_null_sub = FSRSGrantFactory(
        fain=fabs_non_null_sub.fain.replace('-', ''),
        federal_agency_id=None,
        awardee_address_country=int_country.country_code_2_char,
        principle_place_country=dom_country.country_code,
        parent_uei=parent_recipient.uei,
        cfda_numbers='00.001 CFDA 1; 00.002 CFDA 2',
        obligation_date=datetime.now(),
        date_submitted=datetime(2019, 5, 30, 16, 25, 12, 34)
    )
    sub_grant_non_null_sub = FSRSSubgrantFactory(
        parent=grant_non_null_sub,
        awardee_address_country=dom_country.country_code,
        principle_place_country=int_country.country_code_2_char,
        parent_uei=parent_recipient.uei,
        uei_number=recipient.uei,
        subaward_date=datetime.now()
    )
    # FABS Aggregate award
    fabs_agg = PublishedFABSFactory(
        submission_id=sub.submission_id,
        record_type=1,
        unique_award_key='AGG',
        fain='AGG-FAIN-WITH-DASHES',
        awarding_sub_tier_agency_c='1234',
        is_active=True,
        action_date='2020-01-01'
    )
    fabs_agg_2 = PublishedFABSFactory(
        submission_id=sub.submission_id,
        record_type=1,
        unique_award_key='AGG',
        fain='AGG-FAIN-WITH-DASHES',
        awarding_sub_tier_agency_c='1234',
        is_active=True,
        action_date='2020-01-02'
    )
    grant_agg = FSRSGrantFactory(
        fain=fabs_agg.fain.replace('-', ''),
        federal_agency_id='1234',
        awardee_address_country=int_country.country_code_2_char,
        principle_place_country=dom_country.country_code,
        parent_uei=parent_recipient.uei,
        cfda_numbers='00.003 CFDA 3',
        obligation_date=datetime.now(),
        date_submitted=datetime(2019, 5, 30, 16, 25, 12, 34)
    )
    sub_grant_agg = FSRSSubgrantFactory(
        parent=grant_agg,
        awardee_address_country=dom_country.country_code,
        principle_place_country=int_country.country_code,
        parent_uei=parent_recipient.uei.lower(),
        uei_number=recipient.uei,
        subaward_date=datetime.now()
    )
    sess.add_all([sub, fabs_non_pop_subtier, fabs_non_pop_subtier_2, grant_non_pop_subtier, sub_grant_non_pop_subtier,
                  fabs_non_null_sub, fabs_non_null_sub_2, grant_non_null_sub, sub_grant_non_null_sub, fabs_agg,
                  fabs_agg_2, grant_agg, sub_grant_agg])
    sess.commit()

    # Gather the sql
    populate_subaward_table(sess, 'grant_service', ids=[grant_agg.id, grant_non_pop_subtier.id, grant_non_null_sub.id])

    # Get the records
    grants_results = sess.query(Subaward).order_by(Subaward.award_id).all()
    assert len(grants_results) == 3

    created_at = updated_at = grants_results[0].created_at

    # Expected Results
    # Note: Aggregates should not be linked
    assert compare_grant_results(grants_results[0], fabs_agg, grant_agg, sub_grant_agg, parent_recipient, recipient,
                                 dom_country, int_country, created_at, updated_at) is False
    # Note: If federal_agency_id is blank, no link should happen and FSRS data needs to be updated
    assert compare_grant_results(grants_results[1], fabs_non_null_sub, grant_non_null_sub, sub_grant_non_null_sub,
                                 parent_recipient, recipient, dom_country, int_country, created_at, updated_at) is False
    assert compare_grant_results(grants_results[2], fabs_non_pop_subtier, grant_non_pop_subtier,
                                 sub_grant_non_pop_subtier, parent_recipient, recipient, dom_country, int_country,
                                 created_at, updated_at) is True
def test_failure(database):
    """ Test failure for AwardeeOrRecipientUEI is required where ActionDate is after October 1, 2010, unless the record
        is an aggregate or PII-redacted non-aggregate record (RecordType = 1 or 3) or the recipient is an individual
        (BusinessTypes includes 'P'). For AssistanceType 06, 07, 08, 09, 10, or 11, if the base award (the earliest
        record with the same unique award key) has an ActionDate prior to October 1, 2022, this will produce a warning
        rather than a fatal error.
    """
    # Note: for FABS 31.2.2, we're setting assistance types to 06, 07, 08, 09, 10, or 11 and having the base
    #       actiondate be less than October 1, 2022. This rule will not trigger if those *do* apply.
    #       FABS 31.2.1 *will not* trigger when these apply.

    pub_fabs_1 = PublishedFABSFactory(unique_award_key='before_key',
                                      action_date='20091001',
                                      is_active=True)
    pub_fabs_2 = PublishedFABSFactory(unique_award_key='after_key',
                                      action_date='20230404',
                                      is_active=True)
    pub_fabs_3 = PublishedFABSFactory(unique_award_key='inactive_key',
                                      action_date='20091001',
                                      is_active=False)
    models = [pub_fabs_1, pub_fabs_2, pub_fabs_3]

    fabs_1 = FABSFactory(record_type=2,
                         business_types='AbC',
                         uei=None,
                         action_date='10/02/2010',
                         assistance_type='06',
                         correction_delete_indicatr='',
                         unique_award_key='before_key')
    fabs_2 = FABSFactory(record_type=5,
                         business_types='aBc',
                         uei=None,
                         action_date='10/02/2010',
                         assistance_type='07',
                         correction_delete_indicatr='C',
                         unique_award_key='before_key')
    fabs_3 = FABSFactory(record_type=4,
                         business_types='AbC',
                         uei='',
                         action_date='10/02/2010',
                         assistance_type='08',
                         correction_delete_indicatr='c',
                         unique_award_key='before_key')
    fabs_4 = FABSFactory(record_type=5,
                         business_types='aBc',
                         uei='',
                         action_date='10/02/2010',
                         assistance_type='09',
                         correction_delete_indicatr=None,
                         unique_award_key='before_key')
    fabs_5 = FABSFactory(record_type=2,
                         business_types='AbC',
                         uei='',
                         action_date='10/02/2010',
                         assistance_type='06',
                         correction_delete_indicatr='',
                         unique_award_key='inactive_key')
    fabs_6 = FABSFactory(record_type=2,
                         business_types='AbC',
                         uei='',
                         action_date='10/02/2010',
                         assistance_type='06',
                         correction_delete_indicatr='',
                         unique_award_key='new_key')
    models += [fabs_1, fabs_2, fabs_3, fabs_4, fabs_5, fabs_6]

    errors = number_of_errors(_FILE, database, models=models)
    assert errors == 6