From 49d523acaf18ae5a297c8f37ab8c1251ab8cb81b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20G=C3=A2teau?= Date: Sat, 7 Jan 2017 23:50:13 +0100 Subject: [PATCH 1/2] Unbreak p_merge Expiring the merged project before removing it prevents the removal of the tasks which used to be attached to it. --- yokadi/core/db.py | 8 ++++++-- yokadi/tests/projecttestcase.py | 18 +++++++++++------- yokadi/ycli/projectcmd.py | 1 - 3 files changed, 17 insertions(+), 10 deletions(-) diff --git a/yokadi/core/db.py b/yokadi/core/db.py index 2eab4bf2..46df75e9 100644 --- a/yokadi/core/db.py +++ b/yokadi/core/db.py @@ -64,8 +64,12 @@ def merge(self, session, other): """Merge other into us""" if self is other: raise YokadiException("Cannot merge a project into itself") - for task in other.tasks: - task.project = self + # Do a bulk-update: it's faster than updating each task individually + session.query(Task).filter_by(projectId=other.id).update({Task.projectId: self.id}) + + # Tell SQLAlchemy to forget everything it knows about `other`, + # otherwise it will delete the tasks which were once attached to it + session.expire(other) session.delete(other) diff --git a/yokadi/tests/projecttestcase.py b/yokadi/tests/projecttestcase.py index 64e295b9..008f395d 100644 --- a/yokadi/tests/projecttestcase.py +++ b/yokadi/tests/projecttestcase.py @@ -79,20 +79,24 @@ def testStatus(self): self.assertEqual(project.active, True) def testMerge(self): - # Create project p1 with one associated task - dbutils.addTask("p1", "t1", interactive=False) - - # Create project p2 with one associated task - dbutils.addTask("p2", "t2", interactive=False) + COUNT = 4 + for x in range(COUNT): + dbutils.addTask('p1', 'p1-t{}'.format(x), interactive=False) + dbutils.addTask('p2', 'p2-t{}'.format(x), interactive=False) # Merge p1 into p2 tui.addInputAnswers("y") self.cmd.do_p_merge("p1 p2") - # p2 should have two tasks now + # p2 should have both its tasks and all p1 tasks now project = self.session.query(Project).filter_by(name="p2").one() tasks = set([x.title for x in project.tasks]) - self.assertEquals(tasks, {"t1", "t2"}) + + expected = set() + for x in range(COUNT): + expected.add('p1-t{}'.format(x)) + expected.add('p2-t{}'.format(x)) + self.assertEqual(tasks, expected) # p1 should be gone testutils.assertQueryEmpty(self, self.session.query(Project).filter_by(name="p1")) diff --git a/yokadi/ycli/projectcmd.py b/yokadi/ycli/projectcmd.py index 872ef343..2863958f 100644 --- a/yokadi/ycli/projectcmd.py +++ b/yokadi/ycli/projectcmd.py @@ -139,7 +139,6 @@ def parser_p_merge(self): return parser def do_p_merge(self, line): - raise Exception("Broken for now, going to fix it") session = db.getSession() parser = self.parser_p_merge() args = parser.parse_args(line) From b23796e77056c1810de2b65fbfaec7d7a4706a0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20G=C3=A2teau?= Date: Sat, 7 Jan 2017 23:52:28 +0100 Subject: [PATCH 2/2] Add a way to print the SQL commands --- doc/dev/debug.md | 6 ++++++ yokadi/core/db.py | 3 ++- 2 files changed, 8 insertions(+), 1 deletion(-) create mode 100644 doc/dev/debug.md diff --git a/doc/dev/debug.md b/doc/dev/debug.md new file mode 100644 index 00000000..f6cb73e6 --- /dev/null +++ b/doc/dev/debug.md @@ -0,0 +1,6 @@ +# Debugging + +## Show SQL commands + +If you set the `YOKADI_SQL_DEBUG` environment variable to a value different +from "0", all SQL commands will be printed to stdout. diff --git a/yokadi/core/db.py b/yokadi/core/db.py index 46df75e9..749c125e 100644 --- a/yokadi/core/db.py +++ b/yokadi/core/db.py @@ -289,7 +289,8 @@ def __init__(self, dbFileName, createIfNeeded=True, memoryDatabase=False, update if memoryDatabase: connectionString = "sqlite:///:memory:" - self.engine = create_engine(connectionString) + echo = os.environ.get("YOKADI_SQL_DEBUG", "0") != "0" + self.engine = create_engine(connectionString, echo=echo) Session = sessionmaker(bind=self.engine) self.session = Session()