Coverage for CIResults/serializers.py: 91%

389 statements  

« prev     ^ index     » next       coverage.py v7.6.9, created at 2024-12-19 09:20 +0000

1import datetime 

2from django.db import transaction 

3from collections import namedtuple, OrderedDict 

4from rest_framework import serializers 

5 

6from .run_import import JsonResult, RunConfigResults, TestsuiteResults, TestsuiteTestResult 

7from .models import Bug, Component, Build, Test, Machine, RunConfigTag, RunConfig, ReplicationScript, UnknownFailure 

8from .models import TestSuite, TextStatus, IssueFilter, MachineTag, BugTrackerAccount 

9from .models import Issue, BugTracker 

10 

11from shortener.models import Shortener 

12 

13 

14class UpdateMixin(serializers.ModelSerializer): 

15 def get_extra_kwargs(self): 

16 kwargs = super().get_extra_kwargs() 

17 no_update_fields = getattr(self.Meta, "no_update_fields", None) 

18 

19 if self.instance and no_update_fields: 

20 for field in no_update_fields: 

21 kwargs.setdefault(field, {}) 

22 kwargs[field]["read_only"] = True 

23 

24 return kwargs 

25 

26 

27class DynamicFieldsModelSerializer(serializers.ModelSerializer): 

28 """ 

29 A ModelSerializer that takes an additional `extra_fields` argument that 

30 controls which fields should be displayed. Subclass should define a 

31 default_fields list. 

32 """ 

33 default_fields = [] 

34 

35 def __init__(self, *args, **kwargs): 

36 extra_fields_to_serialize = kwargs.pop('extra_fields', None) 

37 super().__init__(*args, **kwargs) 

38 

39 if extra_fields_to_serialize is not None: 

40 # Drop extra fields that are not specified in the `extra_fields` 

41 # argument. 

42 default = set(self.default_fields) 

43 existing = set(self.fields) 

44 for field_name in existing - default - set(extra_fields_to_serialize): 

45 self.fields.pop(field_name) 

46 

47 @classmethod 

48 def extra_fields(cls) -> list[str]: 

49 return list(set(cls.Meta.fields) - set(cls.default_fields)) 

50 

51 

52class RunConfigTagSerializer(serializers.ModelSerializer): 

53 class Meta: 

54 model = RunConfigTag 

55 fields = ('id', 'description', 'url', 'public', '__str__') 

56 read_only_fields = ('public', ) 

57 

58 

59class TestSuiteSerializer(serializers.ModelSerializer): 

60 class Meta: 

61 model = TestSuite 

62 fields = ('id', '__str__') 

63 

64 

65class TestSerializer(serializers.ModelSerializer): 

66 testsuite = TestSuiteSerializer() 

67 

68 class Meta: 

69 model = Test 

70 fields = ('id', 'name', 'testsuite', 'public', 'vetted_on', 'first_runconfig', '__str__') 

71 read_only_fields = ('public', ) 

72 

73 

74class MachineTagSerializer(serializers.ModelSerializer): 

75 class Meta: 

76 model = MachineTag 

77 fields = ('id', 'name', 'public') 

78 read_only_fields = ('added_on', ) 

79 

80 

81class MachineSerializer(serializers.ModelSerializer): 

82 class Meta: 

83 model = Machine 

84 fields = ('id', 'public', 'vetted_on', '__str__') 

85 read_only_fields = ('public', ) 

86 

87 

88class RestViewMachineSerializer(serializers.ModelSerializer): 

89 tags = serializers.SlugRelatedField(many=True, slug_field='name', queryset=MachineTag.objects.all()) 

90 

91 class Meta: 

92 model = Machine 

93 fields = ('id', 'name', 'description', 'public', 'vetted_on', 'aliases', 'tags') 

94 

95 

96class ImportMachineError(Exception): 

97 pass 

98 

99 

100class ImportMachineSerializer(serializers.Serializer): 

101 name = serializers.CharField(required=True) 

102 description = serializers.CharField(required=False, allow_null=True) 

103 public = serializers.BooleanField(required=False, default=False) 

104 vetted = serializers.BooleanField(required=False, default=False) 

105 alias = serializers.CharField(required=False, allow_null=True) 

106 tags = serializers.ListSerializer(child=serializers.CharField(), required=False, default=[]) 

107 

108 @transaction.atomic 

109 def create(self, validated_data): 

110 name = validated_data["name"] 

111 description = validated_data.get("description") 

112 alias = validated_data.get("alias") 

113 if alias: 

114 try: 

115 alias_machine = Machine.objects.get(name=alias) 

116 except Machine.DoesNotExist: 

117 raise ImportMachineError( 

118 "The machine this machine is supposed to alias does not exist. Create it first..." 

119 ) 

120 else: 

121 alias_machine = None 

122 

123 # List tags to show options 

124 tags = {} 

125 for tag in MachineTag.objects.all(): 

126 tags[tag.name] = tag 

127 

128 # Find the list of tags that do not exist yet, and create them 

129 for tag_name in set(validated_data["tags"]) - set(tags.keys()): 

130 tags[tag_name] = MachineTag.objects.create(name=tag_name, public=True) 

131 

132 # Now get/create the machine 

133 try: 

134 machine = Machine.objects.get(name=name) 

135 except Machine.DoesNotExist: 

136 machine = Machine.objects.create(name=name, public=validated_data["public"]) 

137 if validated_data["vetted"]: 

138 machine.vet() 

139 

140 # Description 

141 if description: 

142 machine.description = description 

143 

144 # Alias 

145 if alias_machine is not None: 

146 machine.aliases = alias_machine 

147 

148 # Machine tags 

149 tags_to_add = set(validated_data["tags"]) - set([mt.name for mt in machine.tags.all()]) 

150 for tag_name in tags_to_add: 

151 machine.tags.add(tags[tag_name]) 

152 

153 machine.save() 

154 return machine 

155 

156 

157class TextStatusSerializer(serializers.ModelSerializer): 

158 testsuite = TestSuiteSerializer() 

159 

160 class Meta: 

161 model = TextStatus 

162 fields = ('id', 'name', 'testsuite', '__str__') 

163 

164 

165class IssueFilterSerializer(serializers.ModelSerializer): 

166 tags = RunConfigTagSerializer(many=True) 

167 tests = TestSerializer(many=True) 

168 machine_tags = MachineTagSerializer(many=True) 

169 machines = MachineSerializer(many=True) 

170 statuses = TextStatusSerializer(many=True) 

171 

172 class Meta: 

173 model = IssueFilter 

174 fields = ('id', 'description', 'tags', 'machines', 'machine_tags', 'tests', 'statuses', 

175 'stdout_regex', 'stderr_regex', 'dmesg_regex', 'user_query', '__str__') 

176 read_only_fields = ('added_on', ) 

177 

178 

179class RunConfigSerializer(serializers.ModelSerializer): 

180 tags = serializers.SlugRelatedField(many=True, slug_field='name', queryset=RunConfigTag.objects.all()) 

181 builds = serializers.SlugRelatedField(many=True, slug_field='name', queryset=Build.objects.all()) 

182 

183 class Meta: 

184 model = RunConfig 

185 fields = ('id', 'name', 'tags', 'url', 'added_on', 'builds', 'environment', 'temporary', '__str__') 

186 read_only_fields = ('added_on', ) 

187 

188 def validate(self, data): 

189 components = {} 

190 for build in data["builds"]: 

191 if build.component not in components: 

192 components[build.component] = build 

193 else: 

194 raise serializers.ValidationError( 

195 f"Two builds ({components[build.component]} and {build}) cannot be from the same component" 

196 ) 

197 return data 

198 

199 

200class ImportTestResult(serializers.Serializer): 

201 status = serializers.CharField() 

202 duration = serializers.IntegerField(required=False, allow_null=True) 

203 command = serializers.CharField(required=False, default="") 

204 stdout = serializers.CharField(required=False, allow_null=True) 

205 stderr = serializers.CharField(required=False, allow_null=True) 

206 dmesg = serializers.CharField(required=False, allow_null=True) 

207 url = serializers.URLField(required=False, allow_null=True) 

208 start = serializers.DateTimeField(required=False, default=datetime.datetime.now()) 

209 

210 

211class ImportTestSuiteRunSerializer(serializers.Serializer): 

212 runconfig_name = serializers.CharField(required=True) 

213 test_results = serializers.DictField( 

214 child=serializers.DictField( 

215 child=serializers.DictField(child=ImportTestResult(), required=True), required=True), required=True 

216 ) 

217 test_suite = serializers.CharField(required=True) 

218 

219 @transaction.atomic 

220 def create(self, validated_data): 

221 run_results = [] 

222 try: 

223 test_suite = Build.objects.get(name=validated_data["test_suite"]) 

224 test_suite_results = TestsuiteResults( 

225 runconfig=None, 

226 name=test_suite.component.name, 

227 build=test_suite, 

228 format="json", 

229 version=None, 

230 result_url_pattern=validated_data.get("result_url_pattern") 

231 ) 

232 except Build.DoesNotExist: 

233 raise ValueError(f"Testsuite build {validated_data['test_suite']} does not exist") 

234 for machine_name, raw_machine in validated_data.get("test_results").items(): 

235 for run_id, raw_run in raw_machine.items(): 

236 testsuite_test_results = [] 

237 for test_name, raw_test in raw_run.items(): 

238 testsuite_test_results.append(TestsuiteTestResult( 

239 name=test_name, 

240 status=raw_test["status"], 

241 start_time=raw_test["start"], 

242 duration=datetime.timedelta(seconds=raw_test.get("duration", 0)), 

243 command=raw_test.get("command"), 

244 stdout=raw_test.get("stdout"), 

245 stderr=raw_test.get("stderr"), 

246 dmesg=raw_test.get("dmesg"), 

247 url=raw_test.get("url") 

248 )) 

249 run_results.append(JsonResult(test_suite_results, machine_name, int(run_id), testsuite_test_results)) 

250 

251 rc = RunConfigResults(name=validated_data["runconfig_name"], parsed_results=run_results) 

252 rc.commit_to_db() 

253 return rc 

254 

255 

256class MinimalMachineSerializer(serializers.ModelSerializer): 

257 tags = serializers.StringRelatedField(many=True) 

258 

259 class Meta: 

260 model = Machine 

261 fields = ['name', 'tags'] 

262 

263 

264class MinimalRunConfigSerializer(serializers.ModelSerializer): 

265 tags = serializers.StringRelatedField(many=True) 

266 

267 class Meta: 

268 model = RunConfig 

269 fields = ['name', 'tags'] 

270 

271 

272class UnknownFailureSerializer(DynamicFieldsModelSerializer): 

273 test = serializers.StringRelatedField(source="result.test.name") 

274 status = serializers.StringRelatedField(source="result.status.name") 

275 dmesg = serializers.StringRelatedField(source="result.dmesg") 

276 stdout = serializers.StringRelatedField(source="result.stdout") 

277 stderr = serializers.StringRelatedField(source="result.stderr") 

278 runconfig = MinimalRunConfigSerializer(source="result.ts_run.runconfig") 

279 machine = MinimalMachineSerializer(source="result.ts_run.machine") 

280 testsuite = serializers.StringRelatedField(source="result.ts_run.testsuite.name") 

281 default_fields = ["id", "test", "status", "runconfig", "machine", "testsuite"] 

282 

283 class Meta: 

284 model = UnknownFailure 

285 fields = ["id", "test", "status", "dmesg", "stdout", "stderr", "runconfig", "machine", "testsuite"] 

286 

287 

288class ComponentSerializer(serializers.ModelSerializer): 

289 class Meta: 

290 model = Component 

291 fields = ('id', 'name', 'description', 'url', 'public', '__str__') 

292 

293 

294class BuildSerializer(UpdateMixin, serializers.ModelSerializer): 

295 component = serializers.SlugRelatedField(slug_field='name', queryset=Component.objects.all()) 

296 parents = serializers.SlugRelatedField(many=True, slug_field='name', queryset=Build.objects.all()) 

297 

298 class Meta: 

299 model = Build 

300 fields = ('id', 'name', 'component', 'version', 'added_on', 'parents', 

301 'repo_type', 'branch', 'repo', 'upstream_url', 'parameters', 

302 'build_log', '__str__') 

303 read_only_fields = ('id', 'added_on') 

304 no_update_fields = ['name', 'component'] 

305 

306 

307class BuildMinimalSerializer(serializers.ModelSerializer): 

308 class Meta: 

309 model = Build 

310 fields = ('id', 'name', 'added_on', 'parents', 'upstream_url', '__str__') 

311 read_only_fields = ('id', 'added_on') 

312 

313 

314class RunConfigResultsSerializer(serializers.Serializer): 

315 __str__ = serializers.CharField(max_length=255, read_only=True) 

316 is_failure = serializers.BooleanField(read_only=True) 

317 all_failures_covered = serializers.BooleanField(read_only=True) 

318 bugs_covering = serializers.SerializerMethodField() 

319 

320 def get_bugs_covering(self, obj): 

321 ser = serializers.ListField(child=serializers.CharField(max_length=255, read_only=True)) 

322 return ser.to_representation([b.short_name for b in obj.bugs_covering]) 

323 

324 

325class RunConfigResultsDiffSerializer(serializers.Serializer): 

326 testsuite = serializers.SerializerMethodField() 

327 test = serializers.SerializerMethodField() 

328 machine = serializers.SerializerMethodField() 

329 result_from = RunConfigResultsSerializer(read_only=True) 

330 result_to = RunConfigResultsSerializer(read_only=True) 

331 

332 def get_testsuite(self, obj): 

333 ser = serializers.CharField(max_length=255, read_only=True) 

334 return ser.to_representation(obj.testsuite.name) 

335 

336 def get_test(self, obj): 

337 ser = serializers.CharField(max_length=255, read_only=True) 

338 return ser.to_representation(obj.test.name) 

339 

340 def get_machine(self, obj): 

341 ser = serializers.CharField(max_length=255, read_only=True) 

342 return ser.to_representation(obj.machine.name) 

343 

344 

345class BugTrackerSerializer(serializers.ModelSerializer): 

346 class Meta: 

347 model = BugTracker 

348 fields = ('id', 'name', 'short_name', 'project', 'separator', 'url', 'tracker_type', 'polled', 

349 'components_followed', 'components_followed_since', 'first_response_SLA') 

350 read_only_fields = ('id', 'name', 'short_name', 'project', 'separator', 'url', 'tracker_type', 'polled', 

351 'components_followed', 'components_followed_since', 'first_response_SLA') 

352 

353 

354def serialize_bug(bug, new_comments=None): 

355 def _date_formatter(date_field): 

356 return str(date_field) if date_field is not None else None 

357 

358 resp = { 

359 'url': bug.url, 

360 'bug_id': bug.bug_id, 

361 'title': bug.title, 

362 'description': bug.description, 

363 'tracker': str(bug.tracker), 

364 'created': _date_formatter(bug.created), 

365 'updated': _date_formatter(bug.updated), 

366 'polled': _date_formatter(bug.polled), 

367 'closed': _date_formatter(bug.closed), 

368 'creator': str(bug.creator), 

369 'assignee': str(bug.assignee), 

370 'product': bug.product, 

371 'component': bug.component, 

372 'priority': bug.priority, 

373 'severity': bug.severity, 

374 'features': bug.features_list, 

375 'platforms': bug.platforms_list, 

376 'status': bug.status, 

377 'tags': bug.tags_list, 

378 'custom_fields': bug.custom_fields, 

379 'new_comments': [] 

380 } 

381 if new_comments: 

382 for comm in new_comments: 

383 person = comm.db_object.account.person 

384 author = person.full_name if person.full_name else person.email 

385 resp['new_comments'].append({'author': author, 

386 'created': str(comm.db_object.created_on), 

387 'body': comm.body}) 

388 return resp 

389 

390 

391class BugCompleteSerializer(serializers.ModelSerializer): 

392 tracker = BugTrackerSerializer() 

393 

394 class Meta: 

395 model = Bug 

396 fields = ('id', 'tracker', 'bug_id', 'parent', 'children', 'title', 'description', 'created', 

397 'updated', 'polled', 'closed', 'creator', 'assignee', 'product', 'component', 

398 'priority', 'severity', 'features', 'platforms', 'status', 'tags', 'comments_polled', 

399 'flagged_as_update_pending_on', 'custom_fields') 

400 read_only_fields = ('id', 'tracker', 'bug_id', 'parent', 'children', 'title', 'description', 'created', 

401 'updated', 'polled', 'closed', 'creator', 'assignee', 'product', 'component', 

402 'priority', 'severity', 'features', 'platforms', 'status', 'tags', 'comments_polled', 

403 'flagged_as_update_pending_on', 'custom_fields') 

404 

405 

406class BugMinimalSerializer(serializers.ModelSerializer): 

407 class Meta: 

408 model = Bug 

409 fields = ('id', 'short_name', 'url') 

410 read_only_fields = ('id', 'short_name', 'url') 

411 

412 

413class ExecutionTimeSerializer(serializers.Serializer): 

414 minimum = serializers.DurationField(read_only=True) 

415 maximum = serializers.DurationField(read_only=True) 

416 count = serializers.IntegerField(read_only=True) 

417 

418 

419class RunConfigDiffSerializer(serializers.Serializer): 

420 runcfg_from = RunConfigSerializer(read_only=True) 

421 runcfg_to = RunConfigSerializer(read_only=True) 

422 results = RunConfigResultsDiffSerializer(read_only=True, many=True) 

423 new_tests = serializers.SerializerMethodField() 

424 builds = serializers.SerializerMethodField() 

425 bugs = BugMinimalSerializer(many=True) 

426 status = serializers.CharField(max_length=10, read_only=True) 

427 text = serializers.CharField(max_length=16000, read_only=True) 

428 

429 class BuildDiff2Serializer(serializers.Serializer): 

430 component = ComponentSerializer(read_only=True) 

431 from_build = BuildMinimalSerializer(read_only=True) 

432 to_build = BuildMinimalSerializer(read_only=True) 

433 

434 class RunConfigDiffNewTestsSerializer(serializers.Serializer): 

435 test = TestSerializer() 

436 statuses = serializers.DictField(child=serializers.IntegerField(read_only=True)) 

437 exec_time = ExecutionTimeSerializer(read_only=True) 

438 

439 def get_builds(self, obj): 

440 bd2 = namedtuple('BuildDiff2', ['component', 'from_build', 'to_build']) 

441 build_diffs = {k: bd2(k, v.from_build, v.to_build) for k, v in obj.builds.items()} 

442 dict_ser = serializers.DictField(child=self.BuildDiff2Serializer()) 

443 return dict_ser.to_representation(build_diffs) 

444 

445 def __statuses(self, statuses): 

446 return {k.name: v for k, v in statuses.items()} 

447 

448 def get_new_tests(self, obj): 

449 NT = namedtuple('NewTest', ['test', 'statuses', 'exec_time']) 

450 new_tests = {k: NT(k, {k.name: v for k, v in v.to_statuses.items()}, v.to_exec_times) 

451 for k, v in obj.new_tests.tests.items()} 

452 dict_ser = serializers.DictField(child=self.RunConfigDiffNewTestsSerializer()) 

453 return dict_ser.to_representation(new_tests) 

454 

455 

456class ReplicationScriptSerializer(serializers.ModelSerializer): 

457 created_by = serializers.StringRelatedField() 

458 source_tracker = serializers.StringRelatedField() 

459 destination_tracker = serializers.StringRelatedField() 

460 

461 class Meta: 

462 model = ReplicationScript 

463 fields = ('name', 'created_by', 'created_on', 'source_tracker', 'destination_tracker', 'script') 

464 

465 

466class KnownIssuesSerializer(serializers.Serializer): 

467 id = serializers.IntegerField(read_only=True) 

468 testsuite = serializers.SerializerMethodField() 

469 machine = serializers.SerializerMethodField() 

470 run_id = serializers.SerializerMethodField() 

471 test = serializers.SerializerMethodField() 

472 status = serializers.SerializerMethodField() 

473 url = serializers.SerializerMethodField() 

474 

475 bugs = serializers.SerializerMethodField() 

476 

477 def __init__(self, *args, **kwargs): 

478 super().__init__(*args, **kwargs) 

479 

480 # Cache the serializers for performance reasons 

481 self._char_ser = serializers.CharField(max_length=255, read_only=True) 

482 self._int_ser = serializers.IntegerField(read_only=True) 

483 self._bug_min_ser = BugMinimalSerializer(many=True) 

484 

485 def get_run_id(self, obj): 

486 return self._int_ser.to_representation(obj.result.ts_run.run_id) 

487 

488 def get_testsuite(self, obj): 

489 return self._char_ser.to_representation(obj.result.test.testsuite.name) 

490 

491 def get_test(self, obj): 

492 return self._char_ser.to_representation(obj.result.test.name) 

493 

494 def get_machine(self, obj): 

495 return self._char_ser.to_representation(obj.result.ts_run.machine.name) 

496 

497 def get_status(self, obj): 

498 return self._char_ser.to_representation(obj.result.status.name) 

499 

500 def get_url(self, obj): 

501 return self._char_ser.to_representation(obj.result.url) 

502 

503 def get_bugs(self, obj): 

504 return self._bug_min_ser.to_representation(obj.matched_ifa.issue.bugs.all()) 

505 

506 

507class BugTrackerAccountSerializer(serializers.ModelSerializer): 

508 class Meta: 

509 model = BugTrackerAccount 

510 fields = ('id', 'is_developer') 

511 read_only_fields = ('id', ) 

512 

513 

514class ShortenerSerializer(serializers.ModelSerializer): 

515 class Meta: 

516 model = Shortener 

517 fields = ('id', 'shorthand', 'full', 'added_on', 'last_accessed') 

518 read_only_fields = ('id', 'shorthand', 'full', 'added_on', 'last_accessed') 

519 

520 

521class RateSerializer(serializers.Serializer): 

522 count = serializers.IntegerField() 

523 total = serializers.IntegerField() 

524 percent = serializers.FloatField() 

525 

526 

527class BugSerializer(serializers.ModelSerializer): 

528 class Meta: 

529 model = Bug 

530 fields = ('short_name', 'title', 'url') 

531 read_only_fields = ('short_name', 'title', 'url') 

532 

533 

534class RestIssueSerializer(serializers.ModelSerializer): 

535 class Meta: 

536 model = Issue 

537 fields = ('__all__') 

538 read_only_fields = ('id', 'added_on', 'archived_on', 'runconfigs_covered_count', 'runconfigs_affected_count', 

539 'last_seen', 'added_by', 'archived_by', 'last_seen_runconfig') 

540 

541 bugs = BugSerializer(many=True) 

542 

543 

544class IssueSerializer(serializers.ModelSerializer): 

545 class Meta: 

546 model = Issue 

547 fields = ('id', 'bugs', 'expected') 

548 read_only_fields = ('id', 'bugs', 'expected') 

549 

550 bugs = BugSerializer(many=True) 

551 

552 

553class IssueSerializerMinimal(serializers.ModelSerializer): 

554 class Meta: 

555 model = Issue 

556 fields = ('id', ) 

557 read_only_fields = ('id', ) 

558 

559 

560def serialize_issue_hitrate(issues, minimal=False): 

561 Serializer = IssueSerializerMinimal if minimal else IssueSerializer 

562 

563 ret = [] 

564 for issue, rate in issues.items(): 

565 val = Serializer(issue).data 

566 val['hit_rate'] = RateSerializer(rate).data 

567 ret.append(val) 

568 return ret 

569 

570 

571def serialize_MetricPassRatePerRunconfig(history): 

572 runconfigs = OrderedDict() 

573 for runconfig, _statuses in history.runconfigs.items(): 

574 runconfigs[str(runconfig)] = OrderedDict() 

575 for status, rate in _statuses.items(): 

576 runconfigs[str(runconfig)][str(status)] = RateSerializer(rate).data 

577 

578 statuses = OrderedDict() 

579 for status, _runconfigs in history.statuses.items(): 

580 statuses[str(status)] = OrderedDict() 

581 for runconfig, rate in _runconfigs.items(): 

582 statuses[str(status)][str(runconfig)] = RateSerializer(rate).data 

583 

584 return { 

585 "runconfigs": runconfigs, 

586 "statuses": statuses, 

587 "most_hit_issues": serialize_issue_hitrate(history.most_hit_issues), 

588 "query_key": history.query.query_key, 

589 } 

590 

591 

592class PassRateStatisticsSerializer(serializers.Serializer): 

593 passrate = RateSerializer() 

594 runrate = RateSerializer() 

595 discarded_rate = RateSerializer() 

596 notrun_rate = RateSerializer() 

597 

598 

599def serialize_MetricPassRatePerTest(metric_passrate): 

600 discarded_status = "discarded (expected)" 

601 

602 tests = OrderedDict() 

603 for test, results in metric_passrate.tests.items(): 

604 if results.is_fully_discarded: 

605 tests[str(test)] = { 

606 "status": discarded_status, 

607 "is_pass": False, 

608 "is_run": False, 

609 "duration": str(results.duration), 

610 "issues_hit": serialize_issue_hitrate(results.issue_occurence_rates, minimal=True), 

611 } 

612 else: 

613 tests[str(test)] = { 

614 "status": str(results.overall_result), 

615 "is_pass": results.is_pass, 

616 "is_run": not results.overall_result.is_notrun, 

617 "duration": str(results.duration), 

618 "issues_hit": serialize_issue_hitrate(results.issue_occurence_rates, minimal=True), 

619 } 

620 

621 statuses = OrderedDict() 

622 for status, rate in metric_passrate.statuses.items(): 

623 statuses[str(status)] = RateSerializer(rate).data 

624 

625 return { 

626 "tests": tests, 

627 "statuses": statuses, 

628 "discarded_status": discarded_status, 

629 "machines": [str(m) for m in metric_passrate.machines], 

630 "runconfigs": RunConfigSerializer(metric_passrate.runconfigs, many=True).data, 

631 "raw_statistics": PassRateStatisticsSerializer(metric_passrate.raw_statistics).data, 

632 "statistics": PassRateStatisticsSerializer(metric_passrate.statistics).data, 

633 "most_hit_issues": serialize_issue_hitrate(metric_passrate.most_hit_issues), 

634 "uncovered_failure_rate": RateSerializer(metric_passrate.uncovered_failure_rate).data, 

635 "notrun_rate": RateSerializer(metric_passrate.notrun_rate).data, 

636 "most_interrupting_issues": serialize_issue_hitrate(metric_passrate.most_interrupting_issues), 

637 "unknown_failure_interruption_rate": RateSerializer(metric_passrate.unknown_failure_interruption_rate).data, 

638 "unexplained_interruption_rate": RateSerializer(metric_passrate.unexplained_interruption_rate).data, 

639 "query_key": metric_passrate.query.query_key, 

640 }