StarTripper/ticket_ordering
0
1import re2import numpy as np3from enum import Enum4from typing import DefaultDict5from models import ThreadComment, Ticket, TicketHeuristic6 7 8rng = np.random.default_rng(42)9 10 11class GenerationDifficulty(Enum):12 Easy = 013 Medium = 114 Hard = 215 16 17NAMES = [18 "Aarav", "Emma", "Liam", "Olivia", "Noah", "Ava", "Sophia", "Isabella", "Mia", "Charlotte",19 "Amir", "Fatima", "Hassan", "Layla", "Omar", "Yasmin", "Ali", "Zara", "Ibrahim", "Noor",20 "Wei", "Yuki", "Hiroshi", "Mei", "Sora", "Jin", "Minho", "Haruto", "Aiko", "Ren",21 "Carlos", "Sofia", "Mateo", "Lucia", "Diego", "Valentina", "Juan", "Camila", "Luis", "Elena",22 "Ethan", "Abigail", "James", "Emily", "Benjamin", "Ella", "Lucas", "Scarlett", "Henry", "Grace",23 "Arjun", "Priya", "Rohan", "Ananya", "Vikram", "Sneha", "Kiran", "Isha", "Rahul", "Neha",24 "Leo", "Chloe", "Gabriel", "Lily", "Samuel", "Zoe", "Daniel", "Hannah", "Matthew", "Aria",25 "Alexander", "Nina", "Mikhail", "Anastasia", "Ivan", "Svetlana", "Dmitry", "Olga", "Sergey", "Irina",26 "Kwame", "Ama", "Kofi", "Zuri", "Abena", "Tariq", "Amina", "Malik", "Imani", "Nia",27 "Oscar", "Freja", "Lars", "Ingrid", "Bjorn", "Astrid", "Erik", "Sigrid", "Magnus", "Elin"28]29 30 31DIFFICULTY_UNCERTAINTY_MAP = {32 GenerationDifficulty.Easy: 0.0,33 GenerationDifficulty.Medium: 0.0833,34 GenerationDifficulty.Hard: 0.1666,35}36 37 38CRITERIA = [39 "severity", # how bad40 "fix ease", # how easy to fix41 "scope backend", # backend impact42 "scope frontend", # frontend impact43 "user impact", # perceived impact44]45CRITERIA_DIST_RANGES = {46 "severity": (0.15, 1.9),47 "fix ease": (0.2, 1.7),48 "scope backend": (0.5, 1.0),49 "scope frontend": (0.0, 0.5),50 "user impact": (0.5, 2.9),51}52 53ISSUE_TYPES = [54 ("crash", {55 "severity": 0.9,56 "fix ease": 0.8,57 "user impact": 0.9,58 }),59 ("failure", {60 "severity": 0.75,61 "fix ease": 0.7,62 "user impact": 0.8,63 }),64 ("bug", {65 "severity": 0.4,66 "fix ease": 0.4,67 "user impact": 0.5,68 }),69 ("slowdown", {70 "severity": 0.35,71 "fix ease": 0.5,72 "user impact": 0.6,73 }),74 ("feature request", {75 "severity": 0.05,76 "fix ease": 0.1,77 "user impact": 0.3,78 }),79]80 81SYSTEM_PARTS = [82 ("lookup API", {83 "scope backend": 1.0,84 "scope frontend": 0.1,85 "user impact": 0.1,86 }),87 ("bot API", {88 "scope backend": 1.0,89 "scope frontend": 0.0,90 "user impact": 0.1,91 }),92 ("login system", {93 "scope backend": 0.5,94 "scope frontend": 0.5,95 "user impact": 1.0,96 }),97 ("automatic curation", {98 "scope backend": 0.8,99 "scope frontend": 0.3,100 "user impact": 0.25,101 }),102 ("relationship routing", {103 "scope backend": 0.75,104 "scope frontend": 0.25,105 "user impact": 0.2,106 }),107 ("trend tracker", {108 "scope backend": 0.9,109 "scope frontend": 0.2,110 "user impact": 0.2,111 }),112]113 114MODIFIERS = [115 ("minor", {116 "severity": 0.1,117 "fix ease": 0.9,118 "user impact": 0.1,119 }),120 ("intermittent", {121 "severity": 0.2,122 "fix ease": 0.4,123 "user impact": 0.2,124 }),125 ("random", {126 "severity": 0.5,127 "fix ease": 0.1,128 "user impact": 0.45,129 }),130 ("unexpected", {131 "severity": 0.8,132 "fix ease": 0.5,133 "user impact": 0.75,134 }),135 ("severe", {136 "severity": 0.75,137 "fix ease": 0.5,138 "user impact": 0.8,139 }),140 ("critical", {141 "severity": 0.9,142 "fix ease": 0.5,143 "user impact": 0.9,144 }),145 ("disastrous", {146 "severity": 1.0,147 "fix ease": 0.5,148 "user impact": 1.0,149 }),150]151 152CONTEXTS = [153 "during normal usage",154 "under heavy user load",155 "under heavy API load",156 "fixed time after deployment",157 "after db migration",158]159 160ACTIONS = [161 "pressing action button {random}",162 "opening dashboard",163 "registering on platform",164 "updating {random} multiple times within {random2} seconds",165 "removing post",166 "replying to user",167 "submitting poll",168 "making post"169]170 171NEW_TEMPLATES = [172 "{modifier} {issue} affecting {system} {context}",173 "{issue} in {system} triggered by {action}",174 "{modifier} {issue} when {action} in {system}",175 176 "users experience {modifier} {issue} in {system} {context}",177 "multiple users report {issue} while {action}",178 "user reports {issue} after {action} {context}",179 180 "{action} leads to {modifier} {issue} in {system}",181 "{system} shows {modifier} behavior when {action}",182 183 "{issue} detected in {system} {context}",184 "{modifier} degradation in {system} {context}",185 186 "{issue} in {system} after {action} {context}",187 "{modifier} issue observed in {system} when {action} {context}",188 189 "{modifier} {issue} impacting users during {context}",190 "{issue} causing failures in {system} under {context}",191]192 193 194def fill_action(action_template):195 fillers = [196 "profile", "settings", "feed", "post", "account",197 "preferences", "notification settings"198 ]199 numbers = ["2", "3", "5", "10", "30"]200 201 result = action_template202 result = result.replace("{random}", rng.choice(fillers))203 result = result.replace("{random2}", rng.choice(numbers))204 return result205 206 207def maybe(value, probability=0.7):208 return value if rng.random() < probability else ""209 210 211def clean_text(text):212 text = re.sub(r"\s+", " ", text)213 return text.strip()214 215 216def combine_scores(217 issue, system, modifier,218 219 uncertainty = 0.0,220 importances = {221 "severity": 0.2,222 "fix ease": 0.2,223 "scope backend": 0.2,224 "scope frontend": 0.2,225 "user impact": 0.2,226 }227):228 scores = DefaultDict(float)229 230 for key, value in issue.items():231 scores[key] += value232 for key, value in system.items():233 scores[key] += value234 for key, value in modifier.items():235 scores[key] += value236 237 combined_score = 0.0238 for criteria in CRITERIA:239 _min, _max = CRITERIA_DIST_RANGES[criteria]240 scores[criteria] -= _min241 scores[criteria] /= _max - _min242 243 scores[criteria] += rng.uniform(-uncertainty, +uncertainty)244 245 scores[criteria] = min(scores[criteria], 1.0)246 scores[criteria] = max(scores[criteria], 0.0)247 248 scores[criteria] *= importances[criteria]249 250 combined_score += scores[criteria]251 252 combined_score = min(combined_score, 1.0)253 combined_score = max(combined_score, 0.0)254 255 return combined_score256 257 258def generate_ticket_data(259 uncertainty = 0.0,260 importances = {261 "severity": 0.2,262 "fix ease": 0.2,263 "scope backend": 0.2,264 "scope frontend": 0.2,265 "user impact": 0.2,266 }267):268 template = rng.choice(NEW_TEMPLATES)269 270 issue_name, issue_vals = rng.choice(ISSUE_TYPES) # type: ignore271 system_name, system_vals = rng.choice(SYSTEM_PARTS) # type: ignore272 modifier_name, modifier_vals = rng.choice(MODIFIERS) # type: ignore273 context = rng.choice(CONTEXTS)274 action_template = rng.choice(ACTIONS)275 276 action = fill_action(action_template)277 278 text = template.format(279 modifier=maybe(modifier_name),280 issue=issue_name,281 system=system_name,282 context=context,283 action=action,284 )285 combined_score = combine_scores(286 issue_vals, system_vals, modifier_vals,287 uncertainty=uncertainty,288 importances=importances289 )290 291 return clean_text(text), combined_score292 293 294def generate_problem_statement(difficulty: GenerationDifficulty = GenerationDifficulty.Medium) -> tuple[str, list[Ticket]]:295 rng = np.random.default_rng(42 + difficulty.value)296 297 criteria_str = rng.choice(CRITERIA)298 criteria_importances = {299 "severity": 0.2,300 "fix ease": 0.2,301 "scope backend": 0.2,302 "scope frontend": 0.2,303 "user impact": 0.2,304 }305 for key in criteria_importances:306 if key == criteria_str: criteria_importances[key] = 0.8307 else: criteria_importances[key] = 0.05308 309 uncertainty = DIFFICULTY_UNCERTAINTY_MAP[difficulty]310 311 ids = set()312 tickets_with_scores = []313 for _ in range(rng.integers(5, 10)):314 while True:315 id = rng.integers(0, 15, dtype=int)316 if id not in ids:317 ids.add(id)318 break319 320 text, score = generate_ticket_data(uncertainty, criteria_importances)321 322 ticket = Ticket(323 id=id,324 thread=[ThreadComment(user=rng.choice(NAMES), content=text)],325 heuristic=TicketHeuristic()326 )327 328 tickets_with_scores.append((ticket, score))329 330 tickets_with_scores.sort(key=lambda x: x[1])331 332 tickets = [t for t, _ in tickets_with_scores]333 334 return criteria_str, tickets335 