A private library for storing and exploring a collection of short texts, poems, apophthegms, and other literary pieces. The program provides a simple and interactive way to browse, fetch, and manage a library of texts.
— B. F.
- Project Checklist
- Demo
- Exemplary Screenshots
- Current Status
- Project Structure
- Features
- Installation and Usage
- Customization Guide
- Acknowledgments
- All project updates are tracked and committed using Git.
Command | Used to... |
---|---|
git init |
Start version control for the project. |
git add . |
Stage changes before making a commit. |
git commit -m "description" |
Commit the staged changes with a description. |
git push origin master |
Push committed changes to the remote repository. |
git pull origin master |
Fetch and merge the latest updates from the remote repository. |
For reference, these commands have been applied in multiple commits across the project:
To document the architecture and behavior of Verse Vault, I have created the following diagrams:
The Use-Case Diagram visualizes how users interact with the system, showcasing different features available in the CLI.
📌
- The User interacts with the CLI interface.
- The user can perform actions such as:
- Browsing the library of texts.
- Fetching a random entry.
- Searching for specific texts by title or author.
- Adding new texts to the collection.
- Favoriting entries for later access.
The Component Diagram represents the structural organization of the program, showing how different components communicate.
📌
- The CLI Interface serves as the user’s access point.
- The Library (JSON Storage) holds text data.
- Utility Functions provide helper methods for various operations.
- Display Manager formats terminal output for readability.
- Data Manager handles reading and writing to the storage.
The Activity Diagram illustrates the sequence of actions when a user fetches an entry.
📌
- The process starts with the user selecting the Browse your vault option from the menu, followed by the category Authors or Genres.
- The user decides between:
- Fetching a random entry.
- Searching for a specific entry, by listing them all.
- The system retrieves the selected text and displays it.
- The process ends when the entry is shown and the user hits ENTER.
- Problem Definition
- Target Audience
- Unique Selling Proposition (USP)
- Competitor Analysis
- Core Features
- Monetization Strategy
- Technical Feasibility
- User Experience (UX) Design
- Data Privacy and Security
- Scalability
- Market Validation
- Development Timeline
- Risks and Challenges
- Innovation Potential
- Future Expansion
To define the functional areas of Verse Vault, I conducted Event-Storming, ensuring the project remains modular and extensible. The primary objective of this tool is to help users store, explore, and manage a literary collection interactively.
Domain | Description | Type |
---|---|---|
Vault & Content Management | Stores and organizes short texts, poems, and quotes | Core |
Discovery & Retrieval | Allows users to search, fetch, and randomize entries | Core |
User Customization | Enables users to modify the database, add/edit texts | Supporting |
Styling & Display | Formats terminal output for readability | Supporting |
CLI & Interactive Commands | Handles user interactions via command-line interface | Core |
Favorites & Personalization | Lets users mark preferred texts for quick access | Supporting |
Based on an event-storming process, the chart found in media highlights core domains.
- CLI & Interactive Input serves as the main user interface.
- Vault & Management handles text storage and retrieval.
- Discovery & Fetch helps users explore the content.
- Styling & Display ensure readable output.
- Personalization & Customization enhance the user experience.
To ensure high performance, maintainability, and code quality, Verse Vault was analyzed using Flake8 and additional metrics.
I ran a Flake8 analysis, focusing on:
- Unused Imports (
F401
): Identifies unnecessary imports that can be removed to optimize performance. - Trailing Whitespace & Blank Lines (
W291
,W293
,W391
): Minor stylistic issues that were cleaned up. - Indentation Issues (
E111
,E117
): Ensured all indentation is consistent and follows PEP-8 guidelines. - Blank Line Formatting (
E302
,E305
,E303
): Adjusted blank lines for readability and structure.
Pytest focused on:
--cov=utils
→ Measures coverage for utility functions.--cov=library
→ Measures coverage for JSON-based text storage.--cov-report=term
→ Displays coverage summary in the terminal.--cov-report=html
→ Generates an HTML report with detailed results.
Still a long way from the 80% goal, but the tested sections are most vital.
- Removed unused imports, to improve clarity.
- Fixed all whitespace and indentation errors, to ensure a clean structure.
Here are five improvements made to the project that contribute to clean code principles:
Instead of generic names like data_func()
, the project uses clear, self-explanatory names like:
def load_library(text_type):
"""Load a JSON library file based on text type."""
Why?
- Increases readability.
- Makes the code self-documenting.
- Reduces the need for unnecessary comments.
The codebase follows Single Responsibility Principle (SRP) by splitting functionalities into separate modules:
display.py
→ Handles UI and formatting.fetcher.py
→ Manages data retrieval.data_manager.py
→ Loads and saves JSON data.
Why?
- Easier maintenance.
- Better scalability.
- Clearer structure, making debugging more efficient.
Code is formatted using Flake8 to enforce consistent indentation, spacing, and line length.
Example:
def display_menu():
menu_items = [
"1. Browse your vault",
"2. Random poem",
"3. Random excerpt",
"4. Fill up your vault!",
"5. Show favourites <3",
"6. Exit..."
]
menu_items.sort()
Why?
- Ensures clean, easy-to-read, and maintainable code.
- Avoids style inconsistencies in the project.
Functions gracefully handle missing files instead of crashing.
Example:
def load_library(text_type):
if text_type not in file_map:
raise ValueError(f"Invalid text type '{text_type}' provided.")
Why?
- Prevents unexpected crashes.
- Ensures the program handles errors smoothly.
- Improves user experience.
Implemented pytest tests to ensure functions work as expected.
Used mock tests & coverage tracking to verify code execution.
Example:
def test_load_library():
data = load_library("Poem")
assert isinstance(data, list) # Ensures it returns a list
Why?
- Reduces the risk of bugs.
- Ensures that core functions are working.
- Helps achieve higher test coverage.
For a more detailed reference on Clean Code principles, check out the PDF cheat sheet:
This section demonstrates two refactoring examples taken from the refractoring.py script.
Each example includes:
✔ The original code.
✔ The refactored version.
✔ An explanation of improvements.
🔴 Original Code
def convert_to_euro(amount_in_dollars):
dollar_to_euro_rate = 0.85
return amount_in_dollars * dollar_to_euro_rate
def demo_refact(money_net):
tax_rate = 0.3
money_after_tax = money_net * (1 - tax_rate)
money_in_euro = convert_to_euro(money_after_tax)
return money_in_euro
net_income = 1
money_in_euro = demo_refact(net_income)
print(f"\nMoney in $ before tax: {net_income}")
print(f"\nMoney in Euro after tax: {money_in_euro:.2f}")
✅ Refactored Code
def convert_currency(amount, rate=0.85):
return amount * rate
def apply_tax(income, tax_rate=0.3):
return income * (1 - tax_rate)
def process_conversion(money_net, tax_rate=0.3, conversion_rate=0.85):
net_after_tax = apply_tax(money_net, tax_rate)
money_in_euro = convert_currency(net_after_tax, conversion_rate)
return money_in_euro
net_income = 1
money_in_euro = process_conversion(net_income)
print(f"\nMoney in $ before tax: {net_income}")
print(f"\nMoney in Euro after tax: {money_in_euro:.2f}")
convert_currency()
allows for dynamic currency conversions.apply_tax()
separates the tax logic from the conversion.process_conversion()
applies both tax and currency conversion in one step.
- Functions now have clear responsibilities.
- Easier to update tax rates or conversion rates if needed.
- This approach supports multiple currencies and different tax rates in the future.
🔴 Original Code
print(f"\nMoney in $ before tax: {net_income}")
print(f"\nMoney in Euro after tax: {money_in_euro:.2f}")
✅ Refactored Code
import logging
logging.basicConfig(level=logging.INFO, format="%(message)s")
def display_conversion_results(original, converted, currency="EUR"):
logging.info(f"\nMoney before tax: ${original:.2f}")
logging.info(f"Money after tax in {currency}: €{converted:.2f}")
net_income = 1
money_in_euro = process_conversion(net_income)
display_conversion_results(net_income, money_in_euro)
- Logs are structured and easier to debug.
- Can be redirected to log files or debugging tools.
display_conversion_results()
allows formatted logging.- Easier to modify later for different currencies.
- Allows for cleaner logs and better readability.
Verse Vault integrates a Makefile-based build system and Sphinx for documentation to automate testing and generate documentation.
A Makefile
is used to automate project tasks such as installing dependencies, running tests, generating code coverage, and building documentation.
Run these commands from the project root directory:
Command | Description |
---|---|
make lint |
Run Flake8 linting to check for style errors. |
make test |
Run all tests with pytest . |
make coverage |
Generate a test coverage report. |
make docs |
Build project documentation using Sphinx. |
make clean |
Remove temporary files and caches. |
# Install all dependencies
make install
# Run tests
make test
# Generate test coverage report
make coverage
# Build Sphinx documentation
make docs
The documentation was generated by Sphinx, which is a powerful tool for automating and managing documentation.
What you will find in index.html:
After running make docs
, the HTML documentation is available in:
docs/build/html/index.html
xdg-open docs/build/html/index.html # Linux
open docs/build/html/index.html # macOS
start docs/build/html/index.html # Windows
The project integrates GitHub Actions to automate testing and documentation.
The CI/CD pipeline ensures that every change to the repository is automatically tested and validated before deployment.
The workflow is triggered on:
- Every push to the
master
branch. - Every pull request targeting
master
.
The GitHub Actions workflow file is located in:
.github/workflows/ci.yml
Incorporated unit tests within the codebase to improve stability and maintainability.
- Load Empty Library: Ensures that attempting to load a non-existent JSON file returns an empty list.
- Save and Load Data: Verifies that data can be successfully saved to a JSON file and retrieved without corruption.
- Ornament Formatting: Checks that the terminal ornamentation function returns correctly formatted ASCII text.
- Fetch by Title: Ensures that a literary entry can be retrieved correctly based on title.
- Mocked Data Retrieval: Validates that fetching functions work correctly using mock JSON data.
Before I provide a list of all the code editors/IDE's + favourite shortcuts I have used while working on Verse Vault, I must clarify that Micro was my top pick most of the times, VS Code was used only when I felt the need to use Copilot, which I talk about in the next task, and as for Neovim, it was sort of experimental, but a great learning experience.
-
Favorite Shortcuts:
- Micro:
Ctrl+S
→ Save fileCtrl+Q
→ Quit editorCtrl+E
→ Command mode
- Neovim:
:w
→ Save file:q
→ Quit editordd
→ Delete lineShift + :
→ Command mode
- VS Code:
Ctrl+F
→ Find textCtrl+Shift+P
→ Command PaletteAlt+Shift+F
→ Format document
- Micro:
- AI Integration:
- Copilot was set up inside VS Code for prompt code assistance and debugging. I had already configured it through GitHub, by connecting it to VS Code and installing the plug-in.
- Fabric Framework:
Used it for its sleek and simple solution to leverage LLMs directly from the command line, enhancing workflow efficiency and allowing for swift experimentation.
- only final data structures
- (mostly) side-effect-free functions
- the use of higher-order functions
- functions as parameters and return values
- use closures / anonymous functions
def get_supported_text_types():
return ("Poem", "Short Story", "Apophthegm", "Other")
Tuples are immutable and therefore prevent accidental modifications.
def fetch_favourites(library=None):
if library is None:
library = load_all_libraries()
return tuple(entry for entry in library if entry.get("favourite") == "yes")
Prevents accidental mutations by returning a new tuple.
def format_title(title):
return title.strip().title()
Does not modify the original title, it just returns a formatted version of it. Present only in the README, since it is not needed in the program.
def apply_to_entries(entries, transform_function):
return [transform_function(entry) for entry in entries]
# example
formatted_entries = apply_to_entries(library, format_title)
Allows flexible processing of text entries.
def get_formatter(style):
if style == "uppercase":
return str.upper
elif style == "lowercase":
return str.lower
return str.title
formatter = get_formatter("uppercase")
print(formatter("hello world"))
Returns another function dynamically.
def counter():
count = 0
def increment():
nonlocal count
count += 1
return count
return increment
count_up = counter()
print(count_up())
print(count_up())
The increment()
function remembers count (closure).
sorted_entries = sorted(library, key=lambda x: x["title"])
Uses a lambda function to define a sorting key in a single line.
Above: A brief walkthrough of the core features.
Above: Sub-menu with types of entry.
Above: Sub-menu with types of search.
Above: List of preferred titles.
-
Basic functionality is implemented, including:
- Text browsing and randomization features.
- Modular utilities for managing data, display, and fetching.
-
The database is structured using JSON files stored in the
library/
directory. -
Git is used for version control with the following commands:
git init
,git clone
,git add
,git commit
,git push
,git pull
,git status
,git log
.
-
PyCache files have been ignored using
.gitignore
.
- Every functionality is implemented, including:
- Favoriting, filtered browsing and optimized randomization features.
- Clean, flask8-approved, and modular utils.
- Hand-picked palette and polished ornamentation.
.
├── README.md
└── verse-vault
├── assets
│ └── logo.txt
├── library
│ ├── apophthegm.json # apophthegms
│ ├── other_texts.json # miscellaneous
│ ├── poems.json # poems
│ └── short_stories.json # short stories
├── main.py # entry point
├── requirements.txt # dependencies
└── utils
├── data_manager.py # handles loading and saving JSON files
├── display.py # handles terminal display and styling
├── fetcher.py # internal fetcher
└── __init__.py # module initializer
- Browse: Navigate through your library by category (poems, apophthegms, short stories, others, favorited).
- Fetch: Fetch random entries or search by author/genre/title.
- Customizable: Easily add new texts or modify the existing content.
- Minimalist Design: Simple and clean display focused on readability.
- Python 3.8 or later
- Virtual environment (recommended)
-
Clone the Repository:
git clone https://github.com/bxavaby/verse-vault.git cd verse-vault
-
Install Dependencies:
pip install -r requirements.txt
-
Run the Program:
python main.py
Personalize the ASCII art logo displayed on startup.
- Create Your Logo: Use Text to ASCII to create a custom logo.
- Replace the Logo File:
Update the
logo.txt
in theassets/
directory. - Preview: Run the program to see your new logo.
Tweak your library by:
- Deleting unwanted entries (manually) in the
library/
directory >> select the JSON file >> delete any or all entries manually. - Adding new enties (manually) in the
library/
directory >> select the JSON file >> add new entries manually. - Adding new entries (automatically) run the program >> select option 4 ("Fill up your vault!") >> add the content interactively >> the program appends the new entry to the selected JSON automatically.
- Customizing the terminal colors modify the COLOR_SCHEMES across all files using them (not optimal, will update in the future).
- Picking your preferred titles (manually) in the
library/
directory >> select the JSON file >> edit the value directly (yes/no). - Picking your preferred titles (automatically) when adding a new entry (option 4), the program requests a boolean value (yes/no).
This project is inspired by a deep appreciation for literature, philosophy and the desire to create a personal, private library of short-form texts.