Blood Donor Directory
Find donors of the right group who are medically eligible to give today.
1Introduction: the problem it solves
A local blood donation drive keeps donor phone numbers in a WhatsApp group and a notebook. When a hospital needs O-negative at short notice, somebody scrolls. Worse, people are called who donated three weeks ago and cannot give again yet.
Both problems are one query: the right group, and only those past the ninety-day gap.
A blood donation camp, an NGO, a college NSS unit, or a hospital's volunteer list.
Why it is worth computerising
A local camp's donor list is usually a chat group and a notebook. That is enough until a hospital needs a particular blood group at short notice, at which point somebody scrolls through months of messages while time passes.
The harder problem is eligibility, and it is invisible on paper. A donor who gave three weeks ago cannot give again, and calling them wastes both their time and the camp's. Storing the last donation date lets the computer apply the ninety-day rule to every name at once, so the list produced is not just the right blood group but the people who can actually donate today.
Objectives
- To keep donor contacts in one searchable place rather than in a chat group
- To find donors of a required blood group in seconds
- To exclude anyone who has donated within the last ninety days, automatically
- To include first-time donors, whose last donation date is empty
- To record a donation and report when that donor may next give
2How the job is done today
Before writing anything it is worth asking how the work is handled at present, and why each of those answers falls short. These were examined:
How most local camps run. It works until somebody needs a specific group urgently, and it cannot tell who is eligible today.
Authoritative and national. A local camp still needs its own list of people it can actually call this evening.
Sortable by group, which is half the problem solved. The eligibility gap still has to be worked out by hand for each name.
3Functionalities
- A donor table with the blood group, contact, area and last donation date
- Search by blood group
- Eligibility built into the search — nobody who donated within ninety days is listed
- Donors who have never given are included, since NULL means eligible
- Recording a donation updates the date and reports when they may next give
- Results grouped by area, so the nearest donors are easy to spot
The functions that provide them
| Function | Arguments | What it does |
|---|---|---|
add_donor() | name, group, phone, area | Add a new volunteer, who has not donated yet. |
find_eligible() | group | Everyone of this group who has not donated in the last 90 days. |
record_donation() | donor_id | Stamp today's date against a donor, and say when they may give again. |
4Technical details
| Language | Python 3 |
| Storage | MySQL, through mysql-connector-python |
| Modules used |
|
5How the data is stored
`last_donation` is deliberately allowed to be NULL, and NULL means 'never donated'. The query has to say so explicitly — `last_donation IS NULL OR last_donation <= cut-off` — because a plain comparison silently drops NULL rows, which would hide every first-time donor.
Table: donors
| Field | Type | Description |
|---|---|---|
donor_id | INT (primary key, auto) | Unique number for each donor |
name | VARCHAR(40) (required) | Donor's name |
blood_group | VARCHAR(4) (required) | Blood group, e.g. O+ |
phone | VARCHAR(12) | Contact number |
area | VARCHAR(20) | Locality, used to find the nearest donors |
last_donation | DATE | Date last donated; empty means never, which counts as eligible |
Sample rows in donors
| name | blood_group | phone | area | last_donation |
|---|---|---|---|---|
Ananya Sen | O+ | 9800000001 | Gangtok | 2026-01-10 |
Rohit Das | O+ | 9800000002 | Singtam | NULL |
Meera Rai | A+ | 9800000003 | Rangpo | 2026-08-01 |
Karan Pradhan | O+ | 9800000004 | Gangtok | 2026-08-20 |
6How it works, step by step
add_donor() INSERTs a new volunteer with last_donation left NULL.
date.today() - timedelta(days=90) is the latest donation date that still allows giving today.
One SELECT filtered by group and eligibility, ordered by area.
record_donation() stamps today's date and reports the next eligible date.
7Source code
# ---------------------------------------------------------------------------
# donors.py
#
# Donor directory for the LambdaLab blood donation camp, using Python with
# MySQL.
#
# The point of the program is not the list, it is the eligibility: a donor who
# gave three weeks ago cannot give again, and calling them wastes everybody's
# time.
# ---------------------------------------------------------------------------
import mysql.connector
from datetime import date, timedelta
GAP_DAYS = 90 # a donor must wait this long between donations
db = mysql.connector.connect(host="localhost", user="root",
passwd="camp", database="lambdalab_donors")
cur = db.cursor()
def add_donor(name, group, phone, area):
"""Add a new volunteer, who has not donated yet."""
# last_donation is left NULL, which is how "never donated" is recorded.
# NULL is not the same as zero or an empty string: it means there is no
# value at all.
cur.execute("INSERT INTO donors (name, blood_group, phone, area, last_donation)"
" VALUES (%s, %s, %s, %s, NULL)", (name, group, phone, area))
db.commit()
print("{} added to the {} list.".format(name, group))
def find_eligible(group):
"""Everyone of this group who has not donated in the last 90 days."""
# Today minus ninety days is the latest donation date that still allows
# somebody to give today.
cutoff = date.today() - timedelta(days=GAP_DAYS)
# The IS NULL half of this test is essential. A plain comparison drops rows
# where the value is NULL, so without it every first-time donor — exactly
# the people most willing to help — would silently disappear from the list.
cur.execute("SELECT name, phone, area, last_donation FROM donors"
" WHERE blood_group = %s AND (last_donation IS NULL OR last_donation <= %s)"
" ORDER BY area", (group, cutoff))
rows = cur.fetchall()
print("ELIGIBLE {} DONORS ({} found)".format(group, len(rows)))
print("{:<18}{:<14}{:<14}{:<12}".format("NAME", "PHONE", "AREA", "LAST GAVE"))
for name, phone, area, last in rows:
# A donor with no date shows as "never" rather than as an empty column.
print("{:<18}{:<14}{:<14}{:<12}".format(name, phone, area,
str(last) if last else "never"))
def record_donation(donor_id):
"""Stamp today's date against a donor, and say when they may give again."""
cur.execute("UPDATE donors SET last_donation = %s WHERE donor_id = %s",
(date.today(), donor_id))
db.commit()
print("Thank you. Next donation possible after {}.".format(
date.today() + timedelta(days=GAP_DAYS)))
# --- the program itself ----------------------------------------------------
find_eligible("O+") # who can give O+ blood today
db.close()CREATE DATABASE IF NOT EXISTS lambdalab_donors;
USE lambdalab_donors;
CREATE TABLE donors (
donor_id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(40) NOT NULL,
blood_group VARCHAR(4) NOT NULL,
phone VARCHAR(12),
area VARCHAR(20),
last_donation DATE DEFAULT NULL
);
INSERT INTO donors (name, blood_group, phone, area, last_donation) VALUES
('Ananya Sen', 'O+', '9800000001', 'Gangtok', '2026-01-10'),
('Rohit Das', 'O+', '9800000002', 'Singtam', NULL),
('Meera Rai', 'A+', '9800000003', 'Rangpo', '2026-08-01'),
('Karan Pradhan', 'O+', '9800000004', 'Gangtok', '2026-08-20');The full report as a PDF — cover page, certificate, index, code, output, future scope and references, ready to print and fill in. Or the code on its own as a zip.
8Sample output
This is a real run, not a mock-up — the transcript below is what the program actually printed.
C:\LambdaLab\Projects\Blood-Donors> python donors.py
ELIGIBLE O+ DONORS (2 found)
NAME PHONE AREA LAST GAVE
Ananya Sen 9800000001 Gangtok 2026-01-10
Rohit Das 9800000002 Singtam neverschema.sql into MySQL and change the user and password in the connect() call to your own. The output above is what the queries return against the sample rows in that schema.9Testing
Every case below was actually run and the result recorded as it appeared — including the ones expected to fail.
| Test case | Expected | Actual |
|---|---|---|
| Eligible O+ donors today | Two, one of whom has never given | 2 eligible |
| A donor who gave six days ago | Excluded by the ninety-day rule | 1 extra if the gap is ignored |
| A first-time donor, last_donation NULL | Included | Rohit Das is included |
| Without the IS NULL test in the WHERE | First-timers silently disappear | 1 found instead of 2 |
| A blood group nobody has | No rows, no error | 0 eligible |
Two kinds of case are in there on purpose. The boundary cases test the edge of a rule, where a program is most often wrong by one. The failure cases check that it refuses cleanly and says why, instead of quietly producing a wrong answer.
10Advantages
Set against the ways the job is done today:
- A donor of the right group is found in seconds, not by scrolling
- Nobody ineligible is called, which respects the donor's time
- First-time donors are included rather than filtered out by accident
- The list is ordered by area, so the nearest are contacted first
- The next eligible date is recorded the moment a donation is made
11Future scope
What this version does not do
A report should be honest about its own limits, and each of these is the reason for one of the additions below:
- Age, weight and haemoglobin, which real eligibility also depends on, are not recorded
- The ninety-day gap is applied to everyone; it differs by type of donation
- There is no record of which hospital or camp a donation went to
- Contact details are stored in plain text, with no access control
Proposed enhancements
This is also where you make the project yours. Pick one or two of these, or something nobody here thought of:
- Send a group SMS to everyone eligible in one area
- Record which hospital each donation went to
- A thank-you message on the donor's anniversary
- Age and weight checks, which real eligibility also depends on
- A public page where a hospital can raise a request
12What you may have to teach yourself
CBSE expects some self-learning in a project, and says so. For this one, that means:
- How NULL behaves in a WHERE clause — this project fails quietly if you get it wrong
- The real medical rules for donation gaps in India, which differ by donation type
- Handling personal data responsibly: ask before you store a phone number
13Conclusion
A request for a blood group now produces a list of people who can actually donate today, rather than a list of everybody who once said they would.
The interesting part was the NULL. A donor who has never given has no last-donation date, and a query written without thinking about that would have hidden exactly the people most willing to help.
14References
Every report needs a bibliography. This one used:
- Computer Science with Python, Class XII — the NCERT / CBSE prescribed textbook, for the chapters on database concepts, SQL and interfacing Python with a database
- Computer Science with Python, Class XI — for functions, lists, dictionaries and string handling
- Python 3 documentation — https://docs.python.org/3/
- MySQL 8.0 Reference Manual and the Connector/Python Developer Guide — https://dev.mysql.com/doc/
- CBSE Senior School Curriculum, Computer Science (Subject Code 083) — the project guidelines this report follows
- LambdaLab — https://www.lambdalab.in