from db import query_db, execute_db from datetime import datetime def execute_pipeline(actions, barcode, context): """ Executes the chain of actions defined in the Rule. Returns: {'success': bool, 'message': str, 'data': dict} """ field_values = {} should_save = False for action in actions: atype = action.get('type') # --- MAP (Extract) --- if atype == 'map': start = int(action.get('start', 1)) - 1 end = int(action.get('end', len(barcode))) target = action.get('field') if target: safe_end = min(end, len(barcode)) if start < len(barcode): field_values[target] = barcode[start:safe_end] # --- CLEAN (Format) --- elif atype == 'clean': target = action.get('field') func = action.get('func') if target in field_values: val = str(field_values[target]) if func == 'TRIM': field_values[target] = val.strip() elif func == 'REMOVE_SPACES': field_values[target] = val.replace(" ", "") elif func == 'UPPERCASE': field_values[target] = val.upper() elif func == 'REMOVE_LEADING_ZEROS': field_values[target] = val.lstrip('0') # --- DUPLICATE CHECK (The Gatekeeper) --- elif atype == 'duplicate': target = action.get('field') behavior = action.get('behavior', 'WARN') # Default to WARN val = field_values.get(target) if val: # 1. Check DB same_sess = query_db(f"SELECT id FROM {context['table_name']} WHERE {target} = ? AND session_id = ? AND is_deleted=0", [val, context['session_id']], one=True) other_sess = query_db(f"SELECT id FROM {context['table_name']} WHERE {target} = ? AND is_deleted=0", [val], one=True) is_dup = False dup_msg = "" if same_sess: is_dup = True dup_msg = f"Already scanned in THIS session ({val})" field_values['duplicate_status'] = 'dup_same_session' field_values['duplicate_info'] = 'Duplicate in same session' elif other_sess: is_dup = True dup_msg = f"Previously scanned in another session ({val})" field_values['duplicate_status'] = 'dup_other_session' field_values['duplicate_info'] = 'Duplicate from history' else: field_values['duplicate_status'] = 'normal' field_values['duplicate_info'] = None # 2. Enforce Behavior if is_dup: if behavior == 'BLOCK': # STRICT MODE: Stop immediately. return { 'success': False, 'message': f"⛔ STRICT MODE: {dup_msg}. Entry denied.", 'data': field_values } elif behavior == 'WARN': # WARN MODE: Ask user, unless they already clicked "Yes" if not context.get('confirm_duplicate'): return { 'success': False, 'needs_confirmation': True, 'message': f"⚠️ {dup_msg}", 'data': field_values } # --- USER INPUT (The Gatekeeper) --- elif atype == 'input': # 1. Check if we received the manual data (Weight) from the Save button incoming_data = context.get('extra_data') # 2. If data exists, MERGE it and CONTINUE (Don't stop!) if incoming_data: # Update our main data list with the user's input (e.g. weight=164) field_values.update(incoming_data) continue # <--- RESUME PIPELINE (Goes to next rule, usually SAVE) # 3. If no data, STOP and ask for it return { 'success': False, 'needs_input': True, 'message': 'Opening Details Form...', 'data': field_values } # --- SAVE MARKER --- elif atype == 'save': should_save = True # --- RESULT --- if should_save: try: # Commit to DB cols = ['session_id', 'scanned_by', 'scanned_at'] vals = [context['session_id'], context['user_id'], datetime.now()] for k, v in field_values.items(): cols.append(k) vals.append(v) placeholders = ', '.join(['?'] * len(cols)) sql = f"INSERT INTO {context['table_name']} ({', '.join(cols)}) VALUES ({placeholders})" execute_db(sql, vals) return {'success': True, 'message': 'Saved Successfully', 'data': field_values} except Exception as e: return {'success': False, 'message': f"Database Error: {str(e)}", 'data': field_values} else: return {'success': True, 'message': f"✅ Parsed: {field_values} (No Save Action)", 'data': field_values}