cli.py 4.7 KB

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