From dce6ccc2bd57b94250c8c119edf726440e80c93c Mon Sep 17 00:00:00 2001 From: Tom Kazimiers Date: Wed, 17 Oct 2018 13:09:03 -0400 Subject: [PATCH] DB inegrity check: add test if all mesh faces are triangles This is run now by default for the catmaid_check_db_integrity management command. The --tracing [true|false] and --volumes [true|false] command line parameters now allow to explicitly test only some parts of the database. By default all is tested. Please enter the commit message for your changes. Lines starting See catmaid/CATMAID#1765 --- CHANGELOG.md | 5 ++ .../commands/catmaid_check_db_integrity.py | 52 +++++++++++++++++-- django/applications/catmaid/util.py | 9 ++++ 3 files changed, 61 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5c96b3bded..c16b092404 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -142,6 +142,11 @@ Miscellaneous: - Admin: a user import view is now available to import users from other CATMAID instances. It requires superuser permissions on the remote instance. +- DB integrity check management command: volumes are now checked to make sure + all faces are triangles. The --tracing [true|false] and --volumes [true|false] + command line parameters now allow to explicitly test only some parts of the + database. By default all is tested. + ### Bug fixes diff --git a/django/applications/catmaid/management/commands/catmaid_check_db_integrity.py b/django/applications/catmaid/management/commands/catmaid_check_db_integrity.py index 78883eb624..e676143915 100644 --- a/django/applications/catmaid/management/commands/catmaid_check_db_integrity.py +++ b/django/applications/catmaid/management/commands/catmaid_check_db_integrity.py @@ -7,6 +7,7 @@ from django.core.management.base import BaseCommand, CommandError from django.db import connection from catmaid.models import Project +from catmaid.util import str2bool class Command(BaseCommand): help = ''' @@ -15,6 +16,10 @@ class Command(BaseCommand): def add_arguments(self, parser): parser.add_argument('--project_id', nargs='*', type=int, default=[]) + parser.add_argument("--tracing", type=str2bool, nargs='?', + const=True, default=True, help="Check tracing data.") + parser.add_argument("--volumes", type=str2bool, nargs='?', + const=True, default=True, help="Check volumes data.") def handle(self, *args, **options): project_ids = options['project_id'] @@ -23,17 +28,29 @@ def handle(self, *args, **options): passed = True for project_id in project_ids: - passed = passed and self.check_project(project_id) + passed = passed and self.check_project(project_id, options) if not passed: sys.exit(1) - def check_project(self, project_id): + def check_project(self, project_id, options): if not Project.objects.filter(id=project_id).exists(): raise CommandError('Project with id %s does not exist.' % project_id) - project_passed = True self.stdout.write('Checking integrity of project %s' % project_id) + passed = True + if options['tracing']: + passed = passed and self.check_tracing_data(project_id) + + if options['volumes']: + passed = passed and self.check_volumes(project_id) + + self.stdout.write('') + + return passed + + + def check_tracing_data(self, project_id): self.stdout.write('Check that no connected treenodes are in different skeletons...', ending='') cursor = connection.cursor() cursor.execute(''' @@ -102,9 +119,34 @@ def check_project(self, project_id): project_passed = False row = cursor.fetchone() self.stdout.write('FAILED: node %s in skeleton %s has no path to root' % row) + if test_passed: self.stdout.write('OK') - self.stdout.write('') - return project_passed + def check_volumes(self, project_id): + passed = True + self.stdout.write('Check if all meshes consist only of triangles...', ending='') + cursor = connection.cursor() + cursor.execute(""" + SELECT g.volume_id, + (g.gdump).path[1] as triangle_id, + COUNT(*) as n_points + FROM ( + SELECT v.id AS volume_id, + ST_DumpPoints(geometry) AS gdump + FROM catmaid_volume v + ) AS g + GROUP BY volume_id, (g.gdump).path[1] + HAVING COUNT(*) <> 4; + """) + n_non_triangles = len(list(cursor.fetchall())) + if n_non_triangles > 0: + self.stdout.write('FAILED: found {} non-triangle meshes in project {}'.format( + n_non_triangles, project_id)) + passed = False + else: + self.stdout.write('OK') + passed = passed and True + + return passed diff --git a/django/applications/catmaid/util.py b/django/applications/catmaid/util.py index fc56d16b16..005dd3346c 100644 --- a/django/applications/catmaid/util.py +++ b/django/applications/catmaid/util.py @@ -1,5 +1,6 @@ # -*- coding: utf-8 -*- +import argparse import math from django.utils.encoding import python_2_unicode_compatible @@ -72,3 +73,11 @@ def is_collinear(a, b, c, between=False, eps=epsilon): return not (min(tx, ty, tz) < 0.0 or max(tx, ty, tz) > 1.0) else: return True + +def str2bool(v): + if v.lower() in ('yes', 'true', 't', 'y', '1'): + return True + elif v.lower() in ('no', 'false', 'f', 'n', '0'): + return False + else: + raise argparse.ArgumentTypeError('Boolean value expected.')