|
@@ -1,8 +1,21 @@
|
|
|
from typing import List, Dict, Optional, Set
|
|
|
import json
|
|
|
-from attr import dataclass
|
|
|
+from dataclasses import dataclass, field
|
|
|
import hashlib
|
|
|
|
|
|
+class FNV:
|
|
|
+ INIT64 = int("cbf29ce484222325", 16)
|
|
|
+ PRIME64 = int("100000001b3", 16)
|
|
|
+ MOD64 = 2**64
|
|
|
+
|
|
|
+ @staticmethod
|
|
|
+ def fnv1_64(data: bytes) -> int:
|
|
|
+ hash_value = FNV.INIT64
|
|
|
+ for byte in data:
|
|
|
+ hash_value = (hash_value * FNV.PRIME64) % FNV.MOD64
|
|
|
+ hash_value = hash_value ^ byte
|
|
|
+ return hash_value
|
|
|
+
|
|
|
class DiversionBucket:
|
|
|
def match(self, experiment_context):
|
|
|
raise NotImplementedError("Subclasses must implement this method")
|
|
@@ -10,11 +23,15 @@ class DiversionBucket:
|
|
|
class UidDiversionBucket(DiversionBucket):
|
|
|
def __init__(self, total_buckets: int, buckets: str):
|
|
|
self.total_buckets = total_buckets
|
|
|
- self.buckets = set(map(int, buckets.split(",")))
|
|
|
+ if buckets:
|
|
|
+ self.buckets = set(map(int, buckets.split(",")))
|
|
|
+ else:
|
|
|
+ self.buckets = set()
|
|
|
|
|
|
def match(self, experiment_context):
|
|
|
- uid_hash = int(hashlib.md5(experiment_context.uid.encode()).hexdigest(), 16)
|
|
|
+ uid_hash = int(experiment_context.uid)
|
|
|
bucket = uid_hash % self.total_buckets
|
|
|
+ print(f"Matching UID {experiment_context.uid} with hash {uid_hash} to bucket {bucket} in {self.buckets}")
|
|
|
return bucket in self.buckets
|
|
|
|
|
|
|
|
@@ -45,11 +62,11 @@ class ExperimentContext:
|
|
|
class Domain:
|
|
|
def __init__(self, domain_id, name, flow: int, buckets: str, bucket_type: str, debug_crowd_ids=None, is_default_domain=False, exp_layer_id=None,
|
|
|
debug_users=""):
|
|
|
- self.id = domain_id
|
|
|
+ self.id = int(domain_id)
|
|
|
self.name = name
|
|
|
self.debug_crowd_ids = debug_crowd_ids
|
|
|
self.is_default_domain = is_default_domain
|
|
|
- self.exp_layer_id = exp_layer_id
|
|
|
+ self.exp_layer_id = int(exp_layer_id) if exp_layer_id is not None else None
|
|
|
self.features = []
|
|
|
self.layers = []
|
|
|
self.debug_users = debug_users
|
|
@@ -85,17 +102,12 @@ class Domain:
|
|
|
return False
|
|
|
|
|
|
|
|
|
+@dataclass
|
|
|
class Layer:
|
|
|
id: int
|
|
|
name: str
|
|
|
- experiments: List['Experiment']
|
|
|
- domains: List[Domain]
|
|
|
-
|
|
|
- def __init__(self, layer_id, name):
|
|
|
- self.id = layer_id
|
|
|
- self.name = name
|
|
|
- self.experiments = []
|
|
|
- self.domains = []
|
|
|
+ experiments: List['Experiment'] = field(default_factory=list)
|
|
|
+ domains: List[Domain] = field(default_factory=list)
|
|
|
|
|
|
def add_experiment(self, experiment):
|
|
|
self.experiments.append(experiment)
|
|
@@ -113,9 +125,9 @@ class Experiment:
|
|
|
buckets: str
|
|
|
filter_condition: str
|
|
|
bucket_type: str = "Random"
|
|
|
- debug_user_set: Set[str] = set()
|
|
|
+ debug_user_set: Set[str] = field(default_factory=set)
|
|
|
diversion_bucket: Optional[DiversionBucket] = None
|
|
|
- experiment_versions: List['ExperimentVersion'] = []
|
|
|
+ experiment_versions: List['ExperimentVersion'] = field(default_factory=list)
|
|
|
|
|
|
def add_debug_users(self, users: List[str]):
|
|
|
self.debug_user_set.update(users)
|
|
@@ -150,7 +162,7 @@ class Experiment:
|
|
|
class ExperimentVersion:
|
|
|
def __init__(self, exp_version_id, flow, buckets: str, exp_version_name=None, debug_users: str = '',
|
|
|
config=None, debug_crowd_ids=None):
|
|
|
- self.id = exp_version_id
|
|
|
+ self.id = int(exp_version_id)
|
|
|
self.exp_version_name = exp_version_name
|
|
|
self.config = config
|
|
|
self.debug_crowd_ids = debug_crowd_ids
|
|
@@ -187,7 +199,7 @@ class ExperimentVersion:
|
|
|
class Project:
|
|
|
def __init__(self, project_name=None, project_id=None):
|
|
|
self.project_name = project_name
|
|
|
- self.id = project_id
|
|
|
+ self.id = int(project_id)
|
|
|
self.domains = []
|
|
|
self.layers = []
|
|
|
self.default_domain : Optional[Domain] = None
|