def evaluate(c):
    required=['measurement_id','campaign_id','campaign_version','source','medium','tagging_method','event_name','conversion_definition','attribution_model','attribution_scope','reporting_window','data_source','metric_name','metric_definition_version','known_limitations','evidence_ref']
    complete=all(str(c.get(k,'' )).strip() for k in required)
    needs_new=bool(c.get('semantic_change'))
    if not str(c.get('known_limitations','')).strip():
        state='HOLD_LIMITATIONS'
    elif not str(c.get('evidence_ref','')).strip():
        state='HOLD_EVIDENCE'
    elif c.get('tagging_method') in ('UNKNOWN',''):
        state='HOLD_TAGGING_METHOD'
    elif c.get('baseline_conversion_definition') and c.get('conversion_definition')!=c.get('baseline_conversion_definition') and not c.get('label_version_bumped'):
        state='HOLD_CONVERSION_DEFINITION'
    elif c.get('baseline_attribution_scope') and c.get('attribution_scope')!=c.get('baseline_attribution_scope') and not c.get('label_version_bumped'):
        state='HOLD_ATTRIBUTION_SCOPE'
    elif c.get('offline_import') and not c.get('match_boundary_known'):
        state='HOLD_DATA_ORIGIN'
    elif c.get('semantic_change') and not c.get('label_version_bumped'):
        state='HOLD_METRIC_DEFINITION'
    elif c.get('offline_import') and c.get('cross_channel_semantics_equal') is False:
        state='PARTIAL_COMPARABLE'
    else:
        state='COMPARABLE'
    promotion=state in ('COMPARABLE','PARTIAL_COMPARABLE')
    within=state in ('COMPARABLE','PARTIAL_COMPARABLE')
    cross=state=='COMPARABLE'
    return {'state':state,'within_label':within,'cross_label':cross,'promotion':promotion,'needs_new_label':needs_new,'evidence_complete':complete}
