Building a Custom Project Management Tool to Replace Jira
A workflow for creating a lightweight, customized project management system using a 600-line Python script with SQLite database. This approach replaces complex tools like Jira with a simple, tailored solution that fits specific team needs.

Tools & Prerequisites
Required Tools
Optional Tools
Step-by-Step Guide
Set Up Project Structure
Create the basic project structure with necessary directories and files
Code Example
mkdir project-tracker
cd project-tracker
mkdir templates static
touch app.py requirements.txt
Pro Tip
Use a virtual environment to isolate dependencies: python -m venv venv && source venv/bin/activate
Install Dependencies
Set up Flask and other required Python packages
Code Example
pip install flask sqlite3
echo 'flask==2.0.1' > requirements.txt
Pro Tip
Consider adding flask-login for user authentication and flask-mail for email notifications
Create Database Schema
Design and implement the SQLite database structure for tasks and users
Code Example
CREATE TABLE tasks (
id INTEGER PRIMARY KEY AUTOINCREMENT,
title TEXT NOT NULL,
description TEXT,
status TEXT DEFAULT 'todo',
assignee TEXT,
priority INTEGER DEFAULT 3,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
email TEXT UNIQUE NOT NULL
);
Pro Tip
Use SQLite's built-in timestamp functions for automatic date tracking
Build Flask Application Core
Create the main Flask application with basic routes and database connection
Code Example
from flask import Flask, render_template, request, redirect
import sqlite3
app = Flask(__name__)
def get_db():
conn = sqlite3.connect('tasks.db')
conn.row_factory = sqlite3.Row
return conn
@app.route('/')
def index():
db = get_db()
tasks = db.execute('SELECT * FROM tasks ORDER BY created_at DESC').fetchall()
db.close()
return render_template('index.html', tasks=tasks)
Pro Tip
Use Flask's g object for better database connection management in production
Create HTML Templates
Build simple, functional HTML templates for the user interface
Code Example
<!-- templates/base.html -->
<!DOCTYPE html>
<html>
<head>
<title>Task Tracker</title>
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
</head>
<body>
<div class="container">
{% block content %}{% endblock %}
</div>
</body>
</html>
Pro Tip
Keep the UI simple and focused on functionality over aesthetics initially
Implement CRUD Operations
Add routes for creating, reading, updating, and deleting tasks
Code Example
@app.route('/task/new', methods=['GET', 'POST'])
def new_task():
if request.method == 'POST':
db = get_db()
db.execute('INSERT INTO tasks (title, description, assignee) VALUES (?, ?, ?)',
[request.form['title'], request.form['description'], request.form['assignee']])
db.commit()
db.close()
return redirect('/')
return render_template('new_task.html')
Pro Tip
Add input validation and error handling for production use
Add Search and Filtering
Implement basic search functionality and status filters
Code Example
@app.route('/search')
def search():
query = request.args.get('q', '')
status = request.args.get('status', '')
db = get_db()
sql = 'SELECT * FROM tasks WHERE 1=1'
params = []
if query:
sql += ' AND (title LIKE ? OR description LIKE ?)'
params.extend([f'%{query}%', f'%{query}%'])
if status:
sql += ' AND status = ?'
params.append(status)
tasks = db.execute(sql, params).fetchall()
db.close()
return render_template('index.html', tasks=tasks)
Pro Tip
Consider adding full-text search with SQLite's FTS5 extension for better performance
Style the Application
Add basic CSS styling for a clean, usable interface
Code Example
/* static/style.css */
body {
font-family: Arial, sans-serif;
max-width: 1200px;
margin: 0 auto;
padding: 20px;
}
.task-card {
border: 1px solid #ddd;
padding: 15px;
margin-bottom: 10px;
border-radius: 5px;
}
.status-todo { background-color: #f0f0f0; }
.status-in-progress { background-color: #fff3cd; }
.status-done { background-color: #d4edda; }
Pro Tip
Use CSS Grid or Flexbox for responsive layouts
Add Activity Logging
Implement an audit trail to track changes to tasks
Code Example
def log_activity(task_id, user_id, action):
db = get_db()
db.execute('INSERT INTO activity_log (task_id, user_id, action) VALUES (?, ?, ?)',
[task_id, user_id, action])
db.commit()
db.close()
Pro Tip
Use triggers in SQLite to automatically log updates for better performance
Deploy and Test
Deploy the application and test with your team
Code Example
# For development
python app.py
# For production with gunicorn
pip install gunicorn
gunicorn -w 4 -b 0.0.0.0:8000 app:app
Pro Tip
Start with a small pilot group before rolling out to the entire team
This workflow guides you through building a custom project management tool that replaces Jira with a simple Python script. The author successfully replaced their team's Jira instance with a 600-line Python application that uses SQLite for data storage and provides a web interface for task management.
Overview
The solution consists of:
- A Python Flask web application
- SQLite database for data persistence
- Simple HTML/CSS interface
- Basic task management features (create, update, assign, track)
- Custom workflows tailored to team needs
Key Benefits
- Simplicity: No complex configuration or administration
- Customization: Easily modify to fit exact team needs
- Performance: Fast and lightweight
- Cost: Free and self-hosted
- Control: Full ownership of data and functionality
Architecture
The system uses:
- Flask for the web framework
- SQLite for database (single file, no server needed)
- Jinja2 for templating
- Basic CSS for styling
- Optional: JavaScript for enhanced interactivity
Core Features Implemented
- Task creation and editing
- Status tracking (To Do, In Progress, Done)
- User assignment
- Priority levels
- Basic search and filtering
- Simple reporting
- Email notifications (optional)
Database Schema
The SQLite database includes tables for:
- Tasks (id, title, description, status, assignee, priority, created_at, updated_at)
- Users (id, name, email)
- Comments (id, task_id, user_id, content, created_at)
- Activity logs (id, task_id, user_id, action, timestamp)
Deployment Options
- Local development server
- Internal company server
- Cloud VPS (DigitalOcean, Linode, etc.)
- Docker container
- Heroku or similar PaaS
Discussion (0)
Comments coming soon!