cli.py 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. import click
  2. import json
  3. import yaml
  4. from tinydb import Query
  5. from planner import db
  6. from planner.models import *
  7. from planner.utils import Term
  8. @click.group()
  9. @click.option('-v', '--db', type=str, default='./planning.json', show_default=True)
  10. @click.pass_context
  11. def main(ctx, db):
  12. """Basic CLI project-handling"""
  13. ctx.ensure_object(dict)
  14. @main.group()
  15. @click.pass_context
  16. def milestone(ctx):
  17. """Handle milestones"""
  18. ctx.obj['model'] = Milestone
  19. @main.group()
  20. @click.pass_context
  21. def sprint(ctx):
  22. """Handle sprints"""
  23. ctx.obj['model'] = Sprint
  24. @main.group()
  25. @click.pass_context
  26. def item(ctx):
  27. """Handle items"""
  28. ctx.obj['model'] = Item
  29. @main.group()
  30. @click.pass_context
  31. def comment(ctx):
  32. """Handle comments"""
  33. ctx.obj['model'] = Comment
  34. def save_or_display_errors(model, data):
  35. ok, m_doc = model.validate(data)
  36. if ok :
  37. m = model.get(m_doc)
  38. m.save()
  39. click.echo(Term.success(f"{m} was successfully added"))
  40. else:
  41. click.echo(Term.failure(f"Errors : {m_doc}"))
  42. @milestone.command('new')
  43. @click.option('--name', prompt='Milestone name')
  44. @click.option('--description', prompt='Milestone description')
  45. @click.pass_context
  46. def new_milestone(ctx, name, description):
  47. """Add a new milestone"""
  48. model = ctx.obj['model']
  49. data = {
  50. 'name': name,
  51. 'description': description
  52. }
  53. save_or_display_errors(model, data)
  54. @sprint.command('new')
  55. @click.option('--name', prompt='Sprint name')
  56. @click.option('--description', prompt='Sprint description')
  57. @click.option('--milestone_id', prompt='Related milestone ID', type=int)
  58. @click.pass_context
  59. def new_sprint(ctx, name, description, milestone_id):
  60. """Add a new sprint"""
  61. model = ctx.obj['model']
  62. data = {
  63. 'name': name,
  64. 'description': description,
  65. 'milestone_id': milestone_id
  66. }
  67. save_or_display_errors(model, data)
  68. @item.command('new')
  69. @click.option('--name', prompt='Item name')
  70. @click.option('--description', prompt='Item description')
  71. @click.option('--milestone_id', type=int)
  72. @click.option('--sprint_id', type=int)
  73. @click.option('--category', type=str)
  74. @click.pass_context
  75. def new_item(ctx, name, description, milestone_id=None, sprint_id=None, category=None):
  76. """Add a new sprint"""
  77. model = ctx.obj['model']
  78. if not milestone_id and not sprint_id:
  79. parent_type = click.prompt('Related to [M]ilestone or [S]print ?', type=click.Choice(['m', 's']))
  80. if parent_type == 'm':
  81. milestone_id = click.prompt('Related milestone ID', type=int)
  82. elif parent_type == 's':
  83. sprint_id = click.prompt('Related sprint ID', type=int)
  84. if not category:
  85. choices = {
  86. 'limitation': 'l',
  87. 'feature': 'f',
  88. 'constraint': 'c',
  89. 'unknown': 'u'
  90. }
  91. category_choice = click.prompt(
  92. 'Which category ? ({})'.format(', '.join(choices.keys())),
  93. type=click.Choice([v for v in choices.values()])
  94. )
  95. inv_choices = {v: k for k, v in choices.items()}
  96. category = inv_choices[category_choice]
  97. data = {'name': name, 'description': description, 'category': category}
  98. if milestone_id:
  99. data.update({'milestone_id': milestone_id})
  100. if sprint_id:
  101. data.update({'sprint_id': sprint_id})
  102. save_or_display_errors(model, data)
  103. @comment.command('new')
  104. @click.option('--item_id', prompt='ID of item on which to comment', type=int)
  105. @click.option('--text', prompt='Text', type=str)
  106. @click.pass_context
  107. def new_comment(ctx, item_id, text):
  108. """Add a new comment"""
  109. model = ctx.obj['model']
  110. data = {
  111. 'text': text,
  112. 'item_id': item_id,
  113. }
  114. save_or_display_errors(model, data)
  115. @click.command()
  116. @click.pass_context
  117. def list(ctx):
  118. """List"""
  119. model = ctx.obj['model']
  120. list = model.list()
  121. click.echo('\n'.join([i.as_term for i in list]))
  122. @main.command()
  123. @click.argument('short_uuid')
  124. @click.pass_context
  125. def edit(ctx, short_uuid):
  126. """Edit data"""
  127. for model in [Milestone, Sprint, Comment, Item]:
  128. result_list = db.table(model.class_name()).search(
  129. Query().uuid.test(lambda s: s.startswith(short_uuid))
  130. )
  131. if result_list:
  132. break
  133. if result_list:
  134. result = result_list[0]
  135. item = yaml.dump(result_list[0], allow_unicode=True)
  136. edited = click.edit(item)
  137. if edited:
  138. updated_item = yaml.safe_load(edited)
  139. db.table(model.class_name()).update(updated_item, doc_ids=[result.doc_id])
  140. click.echo(Term.success(f"Successfully updated {updated_item}"))
  141. else:
  142. click.echo(Term.failure(f"No matching result found"))
  143. milestone.add_command(list)
  144. sprint.add_command(list)
  145. item.add_command(list)
  146. comment.add_command(list)
  147. if __name__ == '__main__':
  148. main()