Docket – A CLI application
Tags: beginners, programming, python, github
Published: 2022-01-30 10:51:33

Let's build a docket cli application. A docket is just a list of tasks that must be done. It functions similarly to a to-do application in that you may add tasks, browse a list of tasks, and mark them as completed once they are completed.
You must first install Python on your machine. Python is generally installed by default on most current systems. Open a terminal and type the following command to see which version of Python is installed on your machine.

`python3 –version`

It will give you information about the Python version that is currently installed. You can also install Python by visiting this website: https://installpython3.com/.
To begin, make an empty folder in which to save our project's files.
Now, open your favorite code editor and begin developing our program. Open the empty folder we made for our project. Now, activate the Python environment; if you have Python 3.4 or higher, execute the following command.
```$ python -m venv [directory]```
Bingo! Our Python environment is now active. Now, let's create our python file and name it "'docket.py"', but it's not required to use the same name; you're smart, and I know you can come up with a better one.
Before we begin coding in our Python file, we must first generate four other files. Wait! I know you're excited, but let me explain you what kind of files we'll be making and why. So, we'll need to make two text files: one for entering our tasks (task.txt) and another for keeping completed tasks (completed.txt). Let us now discuss the last two files. We must write one “.bat” (docket.bat) file (which will allow us to run the software on Windows) and one “.sh” (docket.sh) file (helps us to run the program on Linux). However, you must include the following code in these files.

`docket.bat`
```
@echo off

Python3 docket.py %1 %2 %3
```

`docket.sh`
```
#!/usr/bin/env bash

Python3 docket.py “$@”
```

Finally! Now take off!!!!  for our python file, which will be used to code our fantastic program. For constructing our application's CLI interface, we will use Python's argparse module. The argparse module simplifies the creation of user-friendly command line interfaces. The argparse module also generates help and usage messages automatically and generates errors when users provide invalid arguments to the program. You may install argparse on your terminal by typing "' pip install argparse"'.

Import argparse and use the code below to open our txt files.
```
import argparse

task = open("task.txt","r+")
completed_task = open("completed.txt", "r+")
```
Create some variables to read lines from and the length of our txt files.
```
task_data = task.readlines()
completed_data = completed_task.readlines()
c=len(completed_data)
t= len(task_data)
```
 Let's begin our argparse module now.
```
parser= argparse.ArgumentParser()
```
We need to add arguments to the CLI in order to accept user input. We basically need two arguments. One is input1 for writing instructions, and the other is input2 for writing assignments. This is how you use argparse to declare variables and take input from the cli.
```
parser.add_argument('input1',
action='store', type=str, nargs='?', default='default')

parser.add_argument('input2', action = 'store',
                    nargs='?',
                    default='-1')

args = parser.parse_args()
```
Let's get started on the conditions for our commands now. First, we specify whether we want to use the default mode or enter the command "'./docket help"', which will display the application's usage.
```
if(args.input1 == 'default' or args.input1 == 'help'):
    print('''Usage :-
$ ./docket add "hello world"    # Add a new item with priority 2 and text "hello world" to the list
$ ./docket ls                   # Show incomplete list items
$ ./docket del INDEX            # Delete the incomplete item with the given index
$ ./docket done INDEX           # Mark the incomplete item with the given index as complete
$ ./docket help                 # Show usage
$ ./docket report               # Statistics''')
```
Next, we'll define our program for adding new items. Put a condition that says if our input1 is "add" and our input2 is default (which is -1 for input2, which helps us write our task), then write and store the input2 string in our task.txt file. The task's text should be enclosed by double quotations (otherwise only the first word is considered as the item text, and the remaining words are treated as different arguments).
```
# Add a new item

elif(args.input1 == 'add' and args.input2 != '-1'):
    task.write(str(args.input2) +"\n")
    print('Added task: '+ (args.input2))
```
The following step is to write code to display a list of all pending items. If input1 is "ls" (which represents a list) and the length of our line in task.txt is larger than 0, then read each line in task.txt with relevant indexes. If your input1 is ls but the length of the line in task.txt is not defined or 0 then print the no pending task message.
```
# List all pending items

elif(args.input1 == 'ls'and t>0):
    for i in range(t,0,-1):
        line = task_data[i-1].strip()
        print(str(i)+'. '+line)
        
elif(args.input1 == 'ls'):
    print("There are no pending tasks!")
```
We've reached the halfway.  Bingo!! Begin by defining our condition for removing an item from a list. Put a condition that says if our input1 is del (which means delete) and our input2 is not default but is an integer larger than 0 and less than the length of the line in task.txt, then delete that index line from task.txt. If the index does not exist, an error message is displayed.
```
# Delete an item

elif(args.input1 == 'del' and args.input2 != '-1' and int(args.input2) <= t and int(args.input2)>0):
    task_data.remove(task_data[int(args.input2)-1])
    task.truncate(0)
    task.seek(0)
    for line in task_data:
        line = line.strip()
        task.write(line+"\n")
    print('Deleted item with index '+(args.input2))

elif(args.input1 == 'del'):
    print("Error: item with index "+str(int(args.input2))+" does not exist. Nothing deleted.")
```
Its time to mark our task as completed.  Lets put a condition if input1 is done and input2 is not default but it is integer greater than 0 and less than equal to the length of line present in task.txt then mark that line as done and remove it from task.txt and store it in completed.txt file, use relevant index to remove and store line. And if index does not exist then show error.
```
# Mark a task as complete

elif(args.input1 == 'done' and args.input2 != '-1' and int(args.input2) <= t and int(args.input2)>0):
    line = task_data[int(args.input2)-1]
    completed_task.write(task_data[int(args.input2)-1])
    task_data.remove(task_data[int(args.input2)-1])
    task.truncate(0)
    task.seek(0)
    for line in task_data:
        line = line.strip()
        task.write(line+"\n")
    print('Marked item as done.')

elif(args.input1 == 'done'):
    print("Error: no incomplete item with index "+str(int(args.input2))+" exists.")
```
Finally! We are now ready to generate our report. Huhhh…
Put a condition that says if our input1 is a report, then show all the data in task.txt and completed.txt.
```
# Generate a Report 

elif(args.input1 == 'report'):
    print("Pending tasks: "+ str(t))
    for i in range(t,0,-1):
        line = task_data[i-1].strip()
        print(str(i)+'. '+ line )
    print("Completed tasks: "+ str(c))
    for i in range(c,0,-1):
        line = completed_data[i-1].strip()
        print(str(i)+'. '+ line )
```
Tada!! Our cli application is now ready to use. You did it, and I'm delighted it came in handy. The complete source code is available on [my github repo](https://github.com/DiyaVj/Docket). Visit it Once.


![party gif](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/spwoieqp5awd4qn3f0sf.gif)