piknik

Unnamed repository; edit this file 'description' to name the repository.
Info | Log | Files | Refs | README | LICENSE

commit 1fbf6912cb99a747b286ce0417c1c9cda1dcb203
parent 29f5171a3885a495a6d122cd09e20fa61f34c3a3
Author: lash <dev@holbrook.no>
Date:   Sun,  1 Jan 2023 14:02:27 +0000

Add dependency setting for issues

Diffstat:
M.gitignore | 1+
MCHANGELOG | 3+++
Mpiknik/basket.py | 18++++++++++++++++++
Mpiknik/error.py | 2+-
Mpiknik/issue.py | 21+++++++++++++++++++--
Mpiknik/render/plain.py | 4++++
Mpiknik/runnable/mod.py | 14+++++++++++++-
Mpiknik/store/__init__.py | 2+-
Atests/test_dep.py | 34++++++++++++++++++++++++++++++++++
9 files changed, 94 insertions(+), 5 deletions(-)

diff --git a/.gitignore b/.gitignore @@ -3,3 +3,4 @@ __pycache__ build/ dist/ *.egg-info +.piknik diff --git a/CHANGELOG b/CHANGELOG @@ -1,3 +1,6 @@ +- 0.2.3 + * Add possibility to set dependencies between issues + * Set default data directory under working directory - 0.2.2 * Add html requirements to default install - 0.2.1 diff --git a/piknik/basket.py b/piknik/basket.py @@ -162,6 +162,24 @@ class Basket: def get_msg(self, issue_id, envelope_callback=None, message_callback=None, post_callback=None): return self.__get_msg(issue_id, envelope_callback=envelope_callback, message_callback=message_callback, post_callback=post_callback) + + def dep(self, issue_id, dependency_issue_id): + r = self.state.get(issue_id) + self.state.get(dependency_issue_id) + o = Issue.from_str(r) + r = o.dep(dependency_issue_id) + self.state.replace(issue_id, contents=str(o)) + return r + + + def undep(self, issue_id, dependency_issue_id): + r = self.state.get(issue_id) + self.state.get(dependency_issue_id) + o = Issue.from_str(r) + r = o.undep(dependency_issue_id) + self.state.replace(issue_id, contents=str(o)) + return r + def assign(self, issue_id, identity): r = self.state.get(issue_id) diff --git a/piknik/error.py b/piknik/error.py @@ -2,7 +2,7 @@ class DeadIssue(Exception): pass -class AlreadyAssignedError(Exception): +class ExistsError(Exception): pass diff --git a/piknik/issue.py b/piknik/issue.py @@ -6,7 +6,7 @@ import datetime # local imports from piknik.identity import Identity from piknik.error import UnknownIdentityError -from piknik.error import AlreadyAssignedError +from piknik.error import ExistsError class Issue: @@ -18,6 +18,7 @@ class Issue: self.title = title self.assigned = [] self.assigned_time = [] + self.dependencies = [] self.owner_idx = 0 @@ -33,17 +34,32 @@ class Issue: if r['owner'] == None or k == r['owner']: r['owner'] = k o.owner_idx = i + for v in r['dependencies']: + o.dep(v) return o def assign(self, identity, t=None): if identity in self.assigned: - raise AlreadyAssignedError(identity) + raise ExistsError(identity) if t == None: t = datetime.datetime.utcnow() self.assigned.append(identity) self.assigned_time.append(t) + + def dep(self, dependency_issue_id): + if dependency_issue_id in self.dependencies: + raise ExistsError(self.id, dependency_issue_id) + self.dependencies.append(dependency_issue_id) + + + def undep(self, dependency_issue_id=None): + if dependency_issue_id == None: + self.dependencies = [] + return + self.dependencies.remove(dependency_issue_id) + def get_assigned(self): return list(zip(self.assigned, self.assigned_time)) @@ -86,6 +102,7 @@ class Issue: 'id': str(self.id), 'title': self.title, 'assigned': {}, + 'dependencies': self.dependencies, 'owner': None, } diff --git a/piknik/render/plain.py b/piknik/render/plain.py @@ -88,6 +88,10 @@ tags: {} #s = '\t' + str(s) + '\n' assigns.append(ss) s += ', '.join(assigns) + '\n' + + if len(issue.dependencies) > 0: + s += 'depends on: ' + s += ', '.join(issue.dependencies) + '\n' self.add(s, accumulator=accumulator) super(Renderer, self).apply_issue(state, issue, tags, accumulator=accumulator) diff --git a/piknik/runnable/mod.py b/piknik/runnable/mod.py @@ -1,10 +1,14 @@ import sys import argparse +import logging from piknik import Basket from piknik import Issue from piknik.store import FileStoreFactory +logging.basicConfig(level=logging.DEBUG) +logg = logging.getLogger() + argp = argparse.ArgumentParser() argp.add_argument('-d', type=str, help='Data directory') @@ -19,6 +23,8 @@ argp.add_argument('-u', '--untag', type=str, action='append', default=[], help=' argp.add_argument('-a', '--assign', type=str, action='append', default=[], help='Assign given identity to issue') argp.add_argument('--unassign', type=str, action='append', default=[], help='Unassign given identity from issue') argp.add_argument('-o', '--owner', type=str, help='Set given identity as owner of issue') +argp.add_argument('--dep', action='append', default=[], type=str, help='Set issue dependency') +argp.add_argument('--undep', action='append', default=[], type=str, help='Remove issue dependency') argp.add_argument('issue_id', type=str, help='Issue id to modify') arg = argp.parse_args(sys.argv[1:]) @@ -51,7 +57,13 @@ def main(): for v in arg.assign: basket.assign(arg.issue_id, v) - + + for v in arg.undep: + basket.undep(arg.issue_id, v) + + for v in arg.dep: + basket.dep(arg.issue_id, v) + if arg.owner: basket.owner(arg.issue_id, arg.owner) diff --git a/piknik/store/__init__.py b/piknik/store/__init__.py @@ -47,7 +47,7 @@ class FileStoreFactory: def __init__(self, directory=None): if directory == None: - directory = os.path.join(os.environ['HOME'], '.piknik') + directory = os.path.join('.', '.piknik') self.directory = directory diff --git a/tests/test_dep.py b/tests/test_dep.py @@ -0,0 +1,34 @@ +# standard imports +import unittest +import logging +import json + +# local imports +from piknik import Issue +from piknik.identity import Identity +from piknik.error import UnknownIdentityError +from piknik.error import ExistsError + +logging.basicConfig(level=logging.DEBUG) +logg = logging.getLogger() + + +class TestAssign(unittest.TestCase): + + def setUp(self): + self.alice = 'F3FAF668E82EF5124D5187BAEF26F4682343F692' + self.bob = 'F645E047EE5BC4E2824C94DB42DC91CFA8ABA02B' + + + def test_dep_basic(self): + one = Issue('foo') + one.dep('bar') + one.dep('baz') + with self.assertRaises(ExistsError): + one.dep('bar') + one.undep('bar') + self.assertEqual(len(one.dependencies), 1) + + +if __name__ == '__main__': + unittest.main()