Skip to content

Commit

Permalink
Merge branch 'sqlalchemy-2.0' into sync
Browse files Browse the repository at this point in the history
  • Loading branch information
agateau committed Sep 3, 2024
2 parents b2004c7 + c6bf84b commit f2379cf
Show file tree
Hide file tree
Showing 12 changed files with 180 additions and 49 deletions.
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
sqlalchemy==1.4.45
sqlalchemy==2.0.32
python-dateutil==2.8.2
colorama==0.4.6
pyreadline3==3.4.1; platform_system == 'Windows'
1 change: 1 addition & 0 deletions scripts/coverage
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ cd $(dirname $0)/..

coverage run --source=yokadi --omit="yokadi/tests/*" -m pytest yokadi/tests/tests.py
coverage report
coverage html
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ def createFileList(sourceDir, *patterns):
# distutils does not support install_requires, but pip needs it to be
# able to automatically install dependencies
install_requires=[
"sqlalchemy ~= 1.4.45",
"sqlalchemy ~= 2.0.32",
"python-dateutil ~= 2.8.2",
"colorama ~= 0.4.6",
"pyreadline3 ~= 3.4.1 ; platform_system == 'Windows'",
Expand Down
21 changes: 12 additions & 9 deletions yokadi/core/db.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,8 @@
from uuid import uuid1

from sqlalchemy import create_engine, inspect
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.ext.associationproxy import association_proxy
from sqlalchemy.orm import scoped_session, sessionmaker, relationship
from sqlalchemy.orm import scoped_session, sessionmaker, relationship, declarative_base
from sqlalchemy.orm.exc import NoResultFound
from sqlalchemy.exc import IntegrityError
from sqlalchemy import Column, Integer, Boolean, Unicode, DateTime, Enum, ForeignKey, UniqueConstraint
Expand Down Expand Up @@ -54,7 +53,7 @@ class Project(Base):
uuid = Column(Unicode, unique=True, default=uuidGenerator, nullable=False)
name = Column(Unicode, unique=True)
active = Column(Boolean, default=True)
tasks = relationship("Task", cascade="all", backref="project")
tasks = relationship("Task", cascade="all", backref="project", cascade_backrefs=False)

def __repr__(self):
return self.name
Expand Down Expand Up @@ -83,14 +82,15 @@ class Keyword(Base):
id = Column(Integer, primary_key=True)
name = Column(Unicode, unique=True)
tasks = association_proxy("taskKeywords", "task")
taskKeywords = relationship("TaskKeyword", cascade="all", backref="keyword")
taskKeywords = relationship("TaskKeyword", cascade="all", backref="keyword", cascade_backrefs=False)

def __repr__(self):
return self.name


class TaskKeyword(Base):
__tablename__ = "task_keyword"
__mapper_args__ = {"confirm_deleted_rows": False}
id = Column(Integer, primary_key=True)
taskId = Column("task_id", Integer, ForeignKey("task.id"), nullable=False)
keywordId = Column("keyword_id", Integer, ForeignKey("keyword.id"), nullable=False)
Expand Down Expand Up @@ -138,8 +138,8 @@ class Task(Base):
status = Column(Enum("new", "started", "done"), default="new")
recurrence = Column(RecurrenceRuleColumnType, nullable=False, default=RecurrenceRule())
projectId = Column("project_id", Integer, ForeignKey("project.id"), nullable=False)
taskKeywords = relationship("TaskKeyword", cascade="all", backref="task")
lock = relationship("TaskLock", cascade="all", backref="task")
taskKeywords = relationship("TaskKeyword", cascade="all", backref="task", cascade_backrefs=False)
lock = relationship("TaskLock", cascade="all", backref="task", cascade_backrefs=False)

def setKeywordDict(self, dct):
"""
Expand Down Expand Up @@ -350,8 +350,7 @@ def createTables(self):
Base.metadata.create_all(self.engine)

def getVersion(self):
inspector = inspect(self.engine)
if not inspector.has_table("config"):
if not self._hasConfigTable():
# There was no Config table in v1
return 1

Expand All @@ -361,12 +360,16 @@ def getVersion(self):
raise YokadiException("Configuration key '%s' does not exist. This should not happen!" % DB_VERSION_KEY)

def setVersion(self, version):
assert self.engine.has_table("config")
assert self._hasConfigTable()
instance = self.session.query(Config).filter_by(name=DB_VERSION_KEY).one()
instance.value = str(version)
self.session.add(instance)
self.session.commit()

def _hasConfigTable(self):
inspector = inspect(self.engine)
return inspector.has_table("config")

def checkVersion(self):
"""Check version and exit if it is not suitable"""
version = self.getVersion()
Expand Down
2 changes: 1 addition & 1 deletion yokadi/tests/bugtestcase.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ def testAdd(self):
expected = ["t1"]
self.assertEqual(result, expected)

kwDict = self.session.query(Task).get(1).getKeywordDict()
kwDict = self.session.get(Task, 1).getKeywordDict()
self.assertEqual(kwDict, dict(_severity=2, _likelihood=4, _bug=123))

for bad_input in ("", # No project
Expand Down
20 changes: 20 additions & 0 deletions yokadi/tests/dbtestcase.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
"""
@author: Aurélien Gâteau <[email protected]>
@license: GPL v3 or later
"""

import unittest

from yokadi.core import db


class DbTestCase(unittest.TestCase):
def setUp(self):
db.connectDatabase("", memoryDatabase=True)
self.session = db.getSession()

def testSetVersion(self):
newVersion = db.DB_VERSION + 1
db._database.setVersion(newVersion)
version = db._database.getVersion()
self.assertEqual(version, newVersion)
91 changes: 87 additions & 4 deletions yokadi/tests/icaltestcase.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@
@author: Sébastien Renard <[email protected]>
@license: GPL v3 or later
"""
from datetime import datetime, timedelta

import icalendar
from yokadi.ycli import tui
from yokadi.ycli.projectcmd import getProjectFromName
from yokadi.yical import yical
from yokadi.core import dbutils
from yokadi.core import db
Expand Down Expand Up @@ -111,8 +115,87 @@ def testKeywordMapping(self):
def testTaskDoneMapping(self):
tui.addInputAnswers("y")
t1 = dbutils.addTask("x", "t1", {})
yical.createVTodoFromTask(t1)
v1 = yical.createVTodoFromTask(t1)

completed = datetime.now()
v1.add("COMPLETED", completed)
yical.updateTaskFromVTodo(t1, v1)
self.assertEqual(t1.status, "done")
self.assertEqual(t1.doneDate, completed)

def testGenerateCal(self):
# Add an inactive project
t1 = dbutils.addTask("p1", "t1", interactive=False)
project = getProjectFromName("p1")
project.active = False

# And an active project with 3 tasks, one of them is done
t2new = dbutils.addTask("p2", "t2new", interactive=False)

t2started = dbutils.addTask("p2", "t2started", interactive=False)
t2started.setStatus("started")

t2done = dbutils.addTask("p2", "t2done", interactive=False)
t2done.setStatus("done")

self.session.commit()

# Generate the calendar
cal = yical.generateCal()

# It should contain only "p2", "t1" and "t2new" and "t2started"
# I am not sure that it should contain "t1" (since its project is not active), but that's the current behavior
summaries = sorted(str(x["SUMMARY"]) for x in cal.subcomponents)
expected = sorted(["p2", f"t1 ({t1.id})", f"t2new ({t2new.id})", f"t2started ({t2started.id})"])

self.assertEqual(summaries, expected)

def testHandlerProcessVTodoModifyTask(self):
# Create a task
task = dbutils.addTask("p1", "t1", interactive=False)
self.session.commit()

# v1["completed"] = datetime.datetime.now()
# yical.updateTaskFromVTodo(t1, v1)
# self.assertEqual(t1.status, "done")
# Create a vTodo to modify the task
modified = datetime.now()
created = modified + timedelta(hours=-1)
vTodo = icalendar.Todo()
vTodo["UID"] = yical.TASK_UID % str(task.id)
vTodo.add("CREATED", created)
vTodo.add("LAST-MODIFIED", modified)
vTodo.add("summary", "new title")

# Process the vTodo
newTaskDict = {}
yical.IcalHttpRequestHandler.processVTodo(newTaskDict, vTodo)

# The task title must have changed
task = dbutils.getTaskFromId(task.id)
self.assertEqual(task.title, "new title")

# newTaskDict must not have changed
self.assertEqual(newTaskDict, {})

def testHandlerProcessVTodoCreateTask(self):
# Create a vTodo to add a new task
modified = datetime.now()
created = modified + timedelta(hours=-1)
vTodo = icalendar.Todo()
vTodo["UID"] = "zogzog"
vTodo.add("summary", "new task")

# Process the vTodo
newTaskDict = {}
yical.IcalHttpRequestHandler.processVTodo(newTaskDict, vTodo)

# The task should be in newTaskDict
newTaskList = list(newTaskDict.items())
self.assertEqual(len(newTaskList), 1)

(uid, taskId) = newTaskList[0]

# And the task can be retrieved
task = dbutils.getTaskFromId(taskId)
self.assertEqual(task.title, "new task")

# And there is only one task
self.assertEqual(self.session.query(db.Task).count(), 1)
Loading

0 comments on commit f2379cf

Please sign in to comment.