A somewhat simple CLI application to manage and track orders, products and couriers for a pop-up coffee shop.
-
A traditional frontend utilizing Typer
-
Data persistence using SQLite3 or CSV file format
-
Imperative mapping to our domain made easy using SQLAlchemy
-
Unit and integration testing powered by pytest
-
CRUD operations for easy data management:
- Create ✔
- Read ✔
- Update ✔
- Delete ✔
- Contains persistent data storage for our CSVs and SQLite database
- Directory for our source code, separated into their own modules
- Our frontend directory for our cli menu logic
- Database configuration such as table mapping
- Where the domain objects are created and configured
- Handler classes for handling filetypes such as CSV
- Reusable utility functions for our frontend
- Directory for creating and running our tests using pytest
- Integration testing to CSV and SQLite storage layers
- Unit testing for our core logic focusing on control flow
git clone https://github.com/carltonlnd/guk-mini-project.git cafe-cli/
cd cafe-cli
python3 -m venv <env>
source ./<env>/bin/activate
pip install -r requirements.txt
- Python 3.10.7+
./cafe --help
This CLI app was worked on over the course of 5 weeks with evolving requirements, the final week containing both week 5 and 6. For this reason I found this project vastly different to working on my own personal projects where time constraints and client requirements are up to my own discretion.
My primary goal when approaching this project was to design my software to be modular. As in I could, rather seamlessly, swap out functionality of the application as required. I originally wanted to have separate frontend interfaces to display this feature in action, but as the frontend was a lot more time consuming, I opted to approach this by instead swapping out the backend api following week 5 and 6 requirements.
I used a repository pattern to design the core functionality of this application. I currently have two repositories that are both built from
an abstract base, one for doing CRUD operations on and persisting to a SQLite database, and one for CSV files. Because these repositories are
built off of an abstraction, it means that any frontend that uses the CSV repository, can also swap in the SQLite repository without changing
anything but initializing a different repository. This is demonstrated in the application by using the --csv
flag to switch to CSV file
format.
During the course of this project I was very undecided on what my application would like at the end, and because of this I ended up not just refactoring code but completely redesigning from the ground up to achieve my design choice. Because of this there is some STRETCH and BONUS goals that were not met, despite being relatively simple to implement.
For base requirements my application meets them all except a single change in the final week. Where we went from using a string to represent an
order status, to instead the status' ID as a foreign key to it's own table in the database. To work around this, the order status is stored and
represented still as a string, but instead in the format of "<id> (<status>)"
.
The biggest pain that I experienced during this project stems, not surprisingly, from my lack of decisiveness and planning early on in the project. Now while I can say this was made difficult as the requirements evolved weekly, setting on concrete goal earlier than week 4 would have helped massively. This is especially true when it comes to implementing tests, as my project's design was only finalized much too late I found myself having to completely scrap tests that were designed off of previous iterations before introducing my implementation of the repository pattern. Currently input validation for the backend database is almost completely reliant on the frontend validating user input first. Ideally I would like this validation to be in place for the backend also, as this is where invalid user input can cause the most damage.
Should time allow, I would love to package this project into it's own binary executable and upload it to allow for easy installation and use. One caveat however is that Python being an interpreted scripting language makes it harder to compile, and as such I need to do some learning on the topic.
While out of scope for our CLI requirements, I think it would be nice to build a simple web application to further solidify my modular design choice. This would feature calls to the backend api which would be almost identical to how the CLI app is being used right now. Especially if I opt to create this backend using FastAPI, as our current frontend uses Typer which is described as "FastAPI's little sibling".
Week 1
As a user I want to:
- create a product and add it to a list
- view all products
- STRETCH update or delete a product
- A product should just be a string containing its name, i.e: "Coke Zero"
- A list of products should be a list of strings , i.e: ["Coke Zero"]
Week 2
As a user I want to:
- create a product or order and add it to a list
- view all products or orders
- STRETCH I want to be able to update or delete a product or order
- A product should just be a string containing its name, i.e: "Coke Zero"
- A list of products should be a list of strings, i.e: ["Coke Zero"]
- An order should be a dict, i.e:
- A list of orders should be a list of dicts, i.e: [{...}.{...}]
{
"customer_name": "John",
"customer_address": "Unit 2, 12 Main Street, LONDON, WH1 2ER",
"customer_phone": "0789887334",
"status": "preparing"
}
Week 3
As a user I want to:
- create a product, courier, or order and add it to a list
- view all products, couriers, or orders
- update the status of an order
- persist my data (products and couriers)
- STRETCH update or delete a product, order, or courier
- A product should just be a string containing its name, i.e: "Coke Zero"
- A list of products should be a list of strings, i.e: ["Coke Zero"]
- A courier should just be a string containing its name, i.e: "John"
- A list of couriers should be a list of strings, i.e: ["John"]
- An order should be a dict, i.e:
- A list of orders should be a list of dicts, i.e: [{...}.{...}]
- Data should be persisted to a .txt file on a new line for each courier or product, ie:
{
"customer_name": "John",
"customer_address": "Unit 2, 12 Main Street, LONDON, WH1 2ER",
"customer_phone": "0789887334",
"courier": 2,
"status": "preparing"
}
John
Claire
Week 4
As a user I want to:
- create a product, courier, or order dictionary and add it to a list
- view all products, couriers, or orders
- update the status of an order
- persist my data
- STRETCH update or delete a product, order, or courier
- BONUS list orders by status or courier
- A product should be a dict, i.e:
- A courier should be a dict, i.e:
- An order should be a dict, i.e:
- Data should be persisted to a .csv file on a new line for each courier, order, or product, ie:
{
"name": "Coke Zero",
"price": 0.8 # Float
}
{
"name": "Bob",
"phone": "0789887889"
}
{
"customer_name": "John",
"customer_address": "Unit 2, 12 Main Street, LONDON, WH1 2ER",
"customer_phone": "0789887334",
"courier": 2, # Courier index
"status": "preparing",
"items": "1, 3, 4" # Product index
}
John,"Unit 2, 12 Main Street, LONDON, WH1 2ER",2,preparing,"1,3,4"
Week 5
As a user I want to:
- create a product or courier and add it to a database table
- create an order and add the order dictionary to a list
- view all products, couriers, or orders
- update the status of an order
- persist my data
- STRETCH update or delete a product, order, or courier
- BONUS list orders by status or courier
- BONUS track my product inventory
- BONUS import/export my entities in CSV format
- A row in the products table should contain the following information:
- A row in the couriers table should contain the following information:
- An order should be a dict, i.e:
- Orders should be persisted to a .csv file on a new line for each order, ie:
{
"id": 4,
"name": "Coke Zero",
"price": 0.8
}
{
"id": 2,
"name": "Bob",
"phone": "0789887889"
}
{
"customer_name": "John",
"customer_address": "Unit 2, 12 Main Street, LONDON, WH1 2ER",
"customer_phone": "0789887334",
"courier": 2, # Courier ID
"status": "preparing",
"items": "1, 3, 4" # Product IDs
}
John,"Unit 2, 12 Main Street, LONDON, WH1 2ER",2,preparing,"1,3,4"
Week 6
As a user I want to:
- create a product, courier, or order and add it to a table
- view all products, couriers, or orders
- update the status of an order
- persist my data in a database
- STRETCH delete or update a product, order, or courier
- BONUS display orders by status or courier
- BONUS CRUD a list of customers
- BONUS track my product inventory
- BONUS import/export my entities in CSV format
- A row in the products table should contain the following information:
- A row in the couriers table should contain the following information:
- A row in the orders table should contain the following information:
- A row in the order_status table should contain the following information:
{
"id": 4,
"name": "Coke Zero",
"price": 0.8
}
{
"id": 2,
"name": "Bob",
"phone": "0789887889"
}
{
"id": 1,
"customer_name": "John",
"customer_address": "Unit 2, 12 Main Street, LONDON, WH1 2ER",
"customer_phone": "0789887334",
"courier": 2, # Courier ID
"status": 1, # Order status ID
"items": "1, 3, 4" # Product IDs
}
{
"id": 1,
"order_status": "preparing"
}