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 |