| Conditions | 39 |
| Total Lines | 183 |
| Code Lines | 140 |
| Lines | 0 |
| Ratio | 0 % |
| Changes | 0 | ||
Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.
For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.
Commonly applied refactorings include:
If many parameters/temporary variables are present:
Complex classes like tool.objects.dupcheck() often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
| 1 | #!/usr/bin/env python |
||
| 389 | @objects.command(short_help="Find duplicates by UUID") |
||
| 390 | @click.option( |
||
| 391 | "--delete-duplicates", |
||
| 392 | "--delete", |
||
| 393 | default=False, |
||
| 394 | is_flag=True, |
||
| 395 | help="Delete found duplicates", |
||
| 396 | ) |
||
| 397 | @click.option( |
||
| 398 | "--do-merge", "--merge", default=False, is_flag=True, help="Merge found duplicates" |
||
| 399 | ) |
||
| 400 | @click.option("--schema", default=None, help="Work on specified schema only") |
||
| 401 | @click.pass_context |
||
| 402 | def dupcheck(ctx, delete_duplicates, do_merge, schema): |
||
| 403 | """Tool to check for duplicate objects. Which should never happen.""" |
||
| 404 | |||
| 405 | def handle_schema(check_schema): |
||
| 406 | dupes = {} |
||
| 407 | dupe_count = 0 |
||
| 408 | count = 0 |
||
| 409 | |||
| 410 | for item in database.objectmodels[check_schema].find(): |
||
| 411 | if item.uuid in dupes: |
||
| 412 | dupes[item.uuid].append(item) |
||
| 413 | dupe_count += 1 |
||
| 414 | else: |
||
| 415 | dupes[item.uuid] = [item] |
||
| 416 | count += 1 |
||
| 417 | |||
| 418 | if len(dupes) > 0: |
||
| 419 | log( |
||
| 420 | dupe_count, |
||
| 421 | "duplicates of", |
||
| 422 | count, |
||
| 423 | "items total of type", |
||
| 424 | check_schema, |
||
| 425 | "found:", |
||
| 426 | ) |
||
| 427 | log(dupes.keys(), pretty=True, lvl=verbose) |
||
| 428 | |||
| 429 | if delete_duplicates: |
||
| 430 | log("Deleting duplicates") |
||
| 431 | for item in dupes: |
||
| 432 | database.objectmodels[check_schema].find_one({"uuid": item}).delete() |
||
| 433 | |||
| 434 | log("Done for schema", check_schema) |
||
| 435 | elif do_merge: |
||
| 436 | |||
| 437 | def merge(a, b, path=None): |
||
| 438 | """merges b into a""" |
||
| 439 | |||
| 440 | if path is None: |
||
| 441 | path = [] |
||
| 442 | for key in b: |
||
| 443 | if key in a: |
||
| 444 | if isinstance(a[key], dict) and isinstance(b[key], dict): |
||
| 445 | merge(a[key], b[key], path + [str(key)]) |
||
| 446 | elif a[key] == b[key]: |
||
| 447 | pass # same leaf value |
||
| 448 | else: |
||
| 449 | log("Conflict at", path, key, ":", a[key], "<->", b[key]) |
||
| 450 | resolve = "" |
||
| 451 | while resolve not in ("a", "b"): |
||
| 452 | resolve = ask("Choose? (a or b)") |
||
| 453 | if resolve == "a": |
||
| 454 | b[key] = a[key] |
||
| 455 | else: |
||
| 456 | a[key] = b[key] |
||
| 457 | else: |
||
| 458 | a[key] = b[key] |
||
| 459 | return a |
||
| 460 | |||
| 461 | log(dupes, pretty=True, lvl=verbose) |
||
| 462 | |||
| 463 | for item in dupes: |
||
| 464 | if len(dupes[item]) == 1: |
||
| 465 | continue |
||
| 466 | ignore = False |
||
| 467 | while len(dupes[item]) > 1 or ignore is False: |
||
| 468 | log(len(dupes[item]), "duplicates found:") |
||
| 469 | for index, dupe in enumerate(dupes[item]): |
||
| 470 | log("Candidate #", index, ":") |
||
| 471 | log(dupe._fields, pretty=True) |
||
| 472 | request = ask("(d)iff, (m)erge, (r)emove, (i)gnore, (q)uit?") |
||
| 473 | if request == "q": |
||
| 474 | log("Done") |
||
| 475 | return |
||
| 476 | elif request == "i": |
||
| 477 | ignore = True |
||
| 478 | break |
||
| 479 | elif request == "r": |
||
| 480 | delete_request = -2 |
||
| 481 | while delete_request == -2 or -1 > delete_request > len( |
||
| 482 | dupes[item] |
||
| 483 | ): |
||
| 484 | delete_request = ask( |
||
| 485 | "Which one? (0-%i or -1 to cancel)" |
||
| 486 | % (len(dupes[item]) - 1), |
||
| 487 | data_type="int", |
||
| 488 | ) |
||
| 489 | if delete_request == -1: |
||
| 490 | continue |
||
| 491 | else: |
||
| 492 | log("Deleting candidate #", delete_request) |
||
| 493 | dupes[item][delete_request].delete() |
||
| 494 | break |
||
| 495 | elif request in ("d", "m"): |
||
| 496 | merge_request_a = -2 |
||
| 497 | merge_request_b = -2 |
||
| 498 | |||
| 499 | while merge_request_a == -2 or -1 > merge_request_a > len( |
||
| 500 | dupes[item] |
||
| 501 | ): |
||
| 502 | merge_request_a = ask( |
||
| 503 | "Merge from? (0-%i or -1 to cancel)" |
||
| 504 | % (len(dupes[item]) - 1), |
||
| 505 | data_type="int", |
||
| 506 | ) |
||
| 507 | if merge_request_a == -1: |
||
| 508 | continue |
||
| 509 | |||
| 510 | while merge_request_b == -2 or -1 > merge_request_b > len( |
||
| 511 | dupes[item] |
||
| 512 | ): |
||
| 513 | merge_request_b = ask( |
||
| 514 | "Merge into? (0-%i or -1 to cancel)" |
||
| 515 | % (len(dupes[item]) - 1), |
||
| 516 | data_type="int", |
||
| 517 | ) |
||
| 518 | if merge_request_b == -1: |
||
| 519 | continue |
||
| 520 | |||
| 521 | log( |
||
| 522 | deepdiff.DeepDiff( |
||
| 523 | dupes[item][merge_request_a]._fields, |
||
| 524 | dupes[item][merge_request_b]._fields, |
||
| 525 | ), |
||
| 526 | pretty=True, |
||
| 527 | ) |
||
| 528 | |||
| 529 | if request == "m": |
||
| 530 | log( |
||
| 531 | "Merging candidates", |
||
| 532 | merge_request_a, |
||
| 533 | "and", |
||
| 534 | merge_request_b, |
||
| 535 | ) |
||
| 536 | |||
| 537 | _id = dupes[item][merge_request_b]._fields["_id"] |
||
| 538 | if not isinstance(_id, bson.objectid.ObjectId): |
||
| 539 | _id = bson.objectid.ObjectId(_id) |
||
| 540 | |||
| 541 | dupes[item][merge_request_a]._fields["_id"] = _id |
||
| 542 | merge( |
||
| 543 | dupes[item][merge_request_b]._fields, |
||
| 544 | dupes[item][merge_request_a]._fields, |
||
| 545 | ) |
||
| 546 | |||
| 547 | log( |
||
| 548 | "Candidate after merge:", |
||
| 549 | dupes[item][merge_request_b]._fields, |
||
| 550 | pretty=True, |
||
| 551 | ) |
||
| 552 | |||
| 553 | store = "" |
||
| 554 | while store not in ("n", "y"): |
||
| 555 | store = ask("Store?") |
||
| 556 | if store == "y": |
||
| 557 | dupes[item][merge_request_b].save() |
||
| 558 | dupes[item][merge_request_a].delete() |
||
| 559 | break |
||
| 560 | |||
| 561 | database = ctx.obj["db"] |
||
| 562 | |||
| 563 | if schema is None: |
||
| 564 | schemata = database.objectmodels.keys() |
||
| 565 | else: |
||
| 566 | schemata = [schema] |
||
| 567 | |||
| 568 | for thing in schemata: |
||
| 569 | handle_schema(thing) |
||
| 570 | |||
| 571 | finish(ctx) |
||
| 572 |