1818from ..utils .segment_util import get_key_value , match_with_regex
1919from ..enums .segment_operand_regex_enum import SegmentOperandRegexEnum
2020from ..enums .segment_operand_value_enum import SegmentOperandValueEnum
21+ from ..enums .segment_operator_value_enum import SegmentOperatorValueEnum
2122from ...logger .core .log_manager import LogManager
2223from ....utils .gateway_service_util import get_from_gateway_service
2324from ....enums .url_enum import UrlEnum
@@ -244,48 +245,42 @@ def extract_result(self, operand_type: str, operand_value: str, tag_value: str):
244245 :param tag_value: The value of the tag to compare against.
245246 :return: The result of the evaluation.
246247 """
248+ result = False
249+
247250 if tag_value is None :
248251 return False
249252
250253 # Ensure operand_value and tag_value are strings
251- operand_value = str (operand_value )
252- tag_value = str (tag_value )
254+ operand_value_str = str (operand_value )
255+ tag_value_str = str (tag_value )
256+
253257 if operand_type == SegmentOperandValueEnum .LOWER_VALUE .value :
254- return operand_value .lower () == tag_value .lower ()
258+ result = operand_value_str .lower () == tag_value_str .lower ()
255259 elif operand_type == SegmentOperandValueEnum .STARTING_ENDING_STAR_VALUE .value :
256- return operand_value in tag_value
260+ result = tag_value_str . find ( operand_value_str ) != - 1
257261 elif operand_type == SegmentOperandValueEnum .STARTING_STAR_VALUE .value :
258- return tag_value .endswith (operand_value )
262+ result = tag_value_str .endswith (operand_value_str )
259263 elif operand_type == SegmentOperandValueEnum .ENDING_STAR_VALUE .value :
260- return tag_value .startswith (operand_value )
264+ result = tag_value_str .startswith (operand_value_str )
261265 elif operand_type == SegmentOperandValueEnum .REGEX_VALUE .value :
262266 try :
263- pattern = re .compile (operand_value )
264- return bool (pattern .search (tag_value ))
267+ pattern = re .compile (operand_value_str )
268+ matcher = pattern .search (tag_value_str )
269+ result = matcher is not None
265270 except re .error :
266- return False
271+ result = False
267272 elif operand_type == SegmentOperandValueEnum .GREATER_THAN_VALUE .value :
268- try :
269- return float (tag_value ) > float (operand_value )
270- except ValueError :
271- return False
273+ result = self .compare_versions (tag_value_str , operand_value_str ) > 0
272274 elif operand_type == SegmentOperandValueEnum .GREATER_THAN_EQUAL_TO_VALUE .value :
273- try :
274- return float (tag_value ) >= float (operand_value )
275- except ValueError :
276- return False
275+ result = self .compare_versions (tag_value_str , operand_value_str ) >= 0
277276 elif operand_type == SegmentOperandValueEnum .LESS_THAN_VALUE .value :
278- try :
279- return float (tag_value ) < float (operand_value )
280- except ValueError :
281- return False
277+ result = self .compare_versions (tag_value_str , operand_value_str ) < 0
282278 elif operand_type == SegmentOperandValueEnum .LESS_THAN_EQUAL_TO_VALUE .value :
283- try :
284- return float (tag_value ) <= float (operand_value )
285- except ValueError :
286- return False
279+ result = self .compare_versions (tag_value_str , operand_value_str ) <= 0
287280 else :
288- return tag_value == operand_value
281+ result = tag_value_str == operand_value_str
282+
283+ return result
289284
290285 def convert_value (self , value ):
291286 # Check if the value is a boolean
@@ -303,3 +298,121 @@ def convert_value(self, value):
303298 except ValueError :
304299 # Return the value as is if it's not a number
305300 return value
301+
302+ def evaluate_string_operand_dsl (self , dsl_operand_value , context : ContextModel , operand_type : SegmentOperatorValueEnum ):
303+ """
304+ Evaluates a given string tag value against a DSL operand value.
305+
306+ :param dsl_operand_value: The DSL operand string (e.g., "contains(\" value\" )").
307+ :param context: The context object containing the value to evaluate.
308+ :param operand_type: The type of operand being evaluated (ip_address, browser_version, os_version).
309+ :return: True if tag value matches DSL operand criteria, false otherwise.
310+ """
311+ operand = str (dsl_operand_value )
312+
313+ # Determine the tag value based on operand type
314+ tag_value = self .get_tag_value_for_operand_type (context , operand_type )
315+
316+
317+ if tag_value is None :
318+ self .log_missing_context_error (operand_type )
319+ return False
320+
321+ operand_type_and_value = self .pre_process_operand_value (operand )
322+ processed_values = self .process_values (operand_type_and_value ["operand_value" ], tag_value )
323+ tag_value = processed_values ["tag_value" ]
324+
325+ return self .extract_result (
326+ operand_type_and_value ["operand_type" ],
327+ processed_values ["operand_value" ].strip ().replace ('"' , '' ),
328+ tag_value
329+ )
330+
331+ def get_tag_value_for_operand_type (self , context : ContextModel , operand_type : SegmentOperatorValueEnum ):
332+ """
333+ Gets the appropriate tag value based on the operand type.
334+
335+ :param context: The context object.
336+ :param operand_type: The type of operand.
337+ :return: The tag value or None if not available.
338+ """
339+ if operand_type == SegmentOperatorValueEnum .IP .value :
340+ return context .get_ip_address ()
341+ elif operand_type == SegmentOperatorValueEnum .BROWSER_VERSION .value :
342+ return self .get_browser_version_from_context (context )
343+ else :
344+ # Default works for OS version
345+ return self .get_os_version_from_context (context )
346+
347+ def get_browser_version_from_context (self , context : ContextModel ):
348+ """
349+ Gets browser version from context.
350+
351+ :param context: The context object.
352+ :return: The browser version or None if not available.
353+ """
354+ if context .get_vwo () is None or context .get_vwo ().get_ua_info () is None or len (context .get_vwo ().get_ua_info ()) == 0 :
355+ return None
356+
357+ user_agent = context .get_vwo ().get_ua_info ()
358+
359+ # Assuming UserAgent dictionary contains browser_version
360+ if "browser_version" in user_agent :
361+ return str (user_agent ["browser_version" ]) if user_agent ["browser_version" ] is not None else None
362+ return None
363+
364+ def get_os_version_from_context (self , context : ContextModel ):
365+ """
366+ Gets OS version from context.
367+
368+ :param context: The context object.
369+ :return: The OS version or None if not available.
370+ """
371+ if context .get_vwo () is None or context .get_vwo ().get_ua_info () is None or len (context .get_vwo ().get_ua_info ()) == 0 :
372+ return None
373+
374+ user_agent = context .get_vwo ().get_ua_info ()
375+ # Assuming UserAgent dictionary contains os_version
376+ if "os_version" in user_agent :
377+ return str (user_agent ["os_version" ]) if user_agent ["os_version" ] is not None else None
378+ return None
379+
380+ def log_missing_context_error (self , operand_type : SegmentOperatorValueEnum ):
381+ """
382+ Logs appropriate error message for missing context.
383+
384+ :param operand_type: The type of operand.
385+ """
386+ if operand_type == SegmentOperatorValueEnum .IP .value :
387+ LogManager .get_instance ().info ("To evaluate IP segmentation, please provide ipAddress in context" )
388+ elif operand_type == SegmentOperatorValueEnum .BROWSER_VERSION .value :
389+ LogManager .get_instance ().info ("To evaluate browser version segmentation, please provide userAgent in context" )
390+ else :
391+ LogManager .get_instance ().info ("To evaluate OS version segmentation, please provide userAgent in context" )
392+
393+ def compare_versions (self , version1 : str , version2 : str ):
394+ """
395+ Compares two version strings using semantic versioning rules.
396+ Supports formats like "1.2.3", "1.0", "2.1.4.5", etc.
397+
398+ :param version1: First version string
399+ :param version2: Second version string
400+ :return: -1 if version1 < version2, 0 if equal, 1 if version1 > version2
401+ """
402+ # Split versions by dots and convert to integers
403+ parts1 = [int (part ) if part .isdigit () else 0 for part in version1 .split ('.' )]
404+ parts2 = [int (part ) if part .isdigit () else 0 for part in version2 .split ('.' )]
405+
406+ # Find the maximum length to handle different version formats
407+ max_length = max (len (parts1 ), len (parts2 ))
408+
409+ for i in range (max_length ):
410+ part1 = parts1 [i ] if i < len (parts1 ) else 0
411+ part2 = parts2 [i ] if i < len (parts2 ) else 0
412+
413+ if part1 < part2 :
414+ return - 1
415+ elif part1 > part2 :
416+ return 1
417+
418+ return 0 # Versions are equal
0 commit comments