RaktaPluse

This commit is contained in:
Flatlogic Bot 2026-02-17 15:39:01 +00:00
parent 67d8eb7e56
commit d86257fa5a
28 changed files with 934 additions and 48 deletions

View File

@ -180,3 +180,6 @@ if EMAIL_USE_SSL:
# https://docs.djangoproject.com/en/5.2/ref/settings/#default-auto-field # https://docs.djangoproject.com/en/5.2/ref/settings/#default-auto-field
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
LOGIN_URL = '/login/'
LOGIN_REDIRECT_URL = '/'

View File

@ -1,5 +1,11 @@
from django.contrib import admin from django.contrib import admin
from .models import Donor, BloodRequest, BloodBank from .models import Donor, BloodRequest, BloodBank, VaccineRecord
@admin.register(VaccineRecord)
class VaccineRecordAdmin(admin.ModelAdmin):
list_display = ('vaccine_name', 'user', 'dose_number', 'date_taken', 'location')
list_filter = ('vaccine_name', 'date_taken')
search_fields = ('vaccine_name', 'user__username', 'location')
@admin.register(Donor) @admin.register(Donor)
class DonorAdmin(admin.ModelAdmin): class DonorAdmin(admin.ModelAdmin):

View File

@ -0,0 +1,30 @@
# Generated by Django 5.2.7 on 2026-02-17 14:37
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('core', '0003_donor_citizenship_no_donor_district_and_more'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='VaccineRecord',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('vaccine_name', models.CharField(max_length=100)),
('dose_number', models.PositiveIntegerField(default=1)),
('date_taken', models.DateField()),
('location', models.CharField(max_length=255)),
('center_name', models.CharField(blank=True, max_length=255, null=True)),
('notes', models.TextField(blank=True, null=True)),
('created_at', models.DateTimeField(auto_now_add=True)),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='vaccine_records', to=settings.AUTH_USER_MODEL)),
],
),
]

View File

@ -0,0 +1,23 @@
# Generated by Django 5.2.7 on 2026-02-17 15:07
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('core', '0004_vaccinerecord'),
]
operations = [
migrations.AddField(
model_name='bloodbank',
name='latitude',
field=models.DecimalField(blank=True, decimal_places=6, max_digits=9, null=True),
),
migrations.AddField(
model_name='bloodbank',
name='longitude',
field=models.DecimalField(blank=True, decimal_places=6, max_digits=9, null=True),
),
]

View File

@ -0,0 +1,23 @@
# Generated by Django 5.2.7 on 2026-02-17 15:28
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('core', '0005_bloodbank_latitude_bloodbank_longitude'),
]
operations = [
migrations.AddField(
model_name='bloodrequest',
name='latitude',
field=models.DecimalField(blank=True, decimal_places=6, max_digits=9, null=True),
),
migrations.AddField(
model_name='bloodrequest',
name='longitude',
field=models.DecimalField(blank=True, decimal_places=6, max_digits=9, null=True),
),
]

View File

@ -1,5 +1,19 @@
from django.db import models from django.db import models
from django.utils import timezone from django.utils import timezone
from django.contrib.auth.models import User
class VaccineRecord(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='vaccine_records')
vaccine_name = models.CharField(max_length=100)
dose_number = models.PositiveIntegerField(default=1)
date_taken = models.DateField()
location = models.CharField(max_length=255)
center_name = models.CharField(max_length=255, null=True, blank=True)
notes = models.TextField(blank=True, null=True)
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return f"{self.vaccine_name} - Dose {self.dose_number} for {self.user.username}"
class Donor(models.Model): class Donor(models.Model):
BLOOD_GROUPS = [ BLOOD_GROUPS = [
@ -38,6 +52,8 @@ class BloodRequest(models.Model):
location = models.CharField(max_length=255) location = models.CharField(max_length=255)
urgency = models.CharField(max_length=10, choices=URGENCY_LEVELS, default='NORMAL') urgency = models.CharField(max_length=10, choices=URGENCY_LEVELS, default='NORMAL')
hospital = models.CharField(max_length=255) hospital = models.CharField(max_length=255)
latitude = models.DecimalField(max_digits=9, decimal_places=6, null=True, blank=True)
longitude = models.DecimalField(max_digits=9, decimal_places=6, null=True, blank=True)
contact_number = models.CharField(max_length=20) contact_number = models.CharField(max_length=20)
required_date = models.DateField(default=timezone.now) required_date = models.DateField(default=timezone.now)
status = models.CharField(max_length=20, default='Active') status = models.CharField(max_length=20, default='Active')
@ -49,6 +65,8 @@ class BloodRequest(models.Model):
class BloodBank(models.Model): class BloodBank(models.Model):
name = models.CharField(max_length=100) name = models.CharField(max_length=100)
location = models.CharField(max_length=255) location = models.CharField(max_length=255)
latitude = models.DecimalField(max_digits=9, decimal_places=6, null=True, blank=True)
longitude = models.DecimalField(max_digits=9, decimal_places=6, null=True, blank=True)
contact_number = models.CharField(max_length=20, null=True, blank=True) contact_number = models.CharField(max_length=20, null=True, blank=True)
is_24_7 = models.BooleanField(default=True) is_24_7 = models.BooleanField(default=True)
stock_a_plus = models.IntegerField(default=0) stock_a_plus = models.IntegerField(default=0)

View File

@ -199,14 +199,18 @@
<li class="{% if request.resolver_match.url_name == 'donor_list' %}active{% endif %}"><a href="{% url 'donor_list' %}"><i class="bi bi-people-fill"></i> Donors</a></li> <li class="{% if request.resolver_match.url_name == 'donor_list' %}active{% endif %}"><a href="{% url 'donor_list' %}"><i class="bi bi-people-fill"></i> Donors</a></li>
<li class="{% if request.resolver_match.url_name == 'blood_request_list' %}active{% endif %}"><a href="{% url 'blood_request_list' %}"><i class="bi bi-megaphone-fill"></i> Blood Requests</a></li> <li class="{% if request.resolver_match.url_name == 'blood_request_list' %}active{% endif %}"><a href="{% url 'blood_request_list' %}"><i class="bi bi-megaphone-fill"></i> Blood Requests</a></li>
<li class="{% if request.resolver_match.url_name == 'blood_bank_list' %}active{% endif %}"><a href="{% url 'blood_bank_list' %}"><i class="bi bi-hospital-fill"></i> Blood Banks</a></li> <li class="{% if request.resolver_match.url_name == 'blood_bank_list' %}active{% endif %}"><a href="{% url 'blood_bank_list' %}"><i class="bi bi-hospital-fill"></i> Blood Banks</a></li>
<li class="{% if request.resolver_match.url_name == 'live_map' %}active{% endif %}"><a href="{% url 'live_map' %}"><i class="bi bi-map text-danger"></i> Live Alerts</a></li>
<li class="{% if request.resolver_match.url_name == 'vaccination_info' %}active{% endif %}"><a href="{% url 'vaccination_info' %}"><i class="bi bi-shield-check"></i> Vaccination</a></li> <li class="{% if request.resolver_match.url_name == 'vaccination_info' %}active{% endif %}"><a href="{% url 'vaccination_info' %}"><i class="bi bi-shield-check"></i> Vaccination</a></li>
{% if user.is_authenticated %}
<li class="{% if 'vaccination_dashboard' in request.resolver_match.url_name or 'add_vaccination' in request.resolver_match.url_name %}active{% endif %}"><a href="{% url 'vaccination_dashboard' %}"><i class="bi bi-journal-check"></i> My Records</a></li>
{% endif %}
<li><a href="/admin/"><i class="bi bi-gear-fill"></i> Settings</a></li> <li><a href="/admin/"><i class="bi bi-gear-fill"></i> Settings</a></li>
</ul> </ul>
<div class="position-absolute bottom-0 w-100 p-4"> <div class="position-absolute bottom-0 w-100 p-4">
<div class="glass-card p-3 bg-opacity-10 border-danger border-opacity-25" style="background: rgba(230, 57, 70, 0.05);"> <div class="glass-card p-3 bg-opacity-10 border-danger border-opacity-25" style="background: rgba(230, 57, 70, 0.05);">
<p class="small text-secondary mb-2">Need Help?</p> <p class="small text-secondary mb-2">Need Help?</p>
<a href="tel:911" class="btn btn-sm btn-danger w-100 fw-bold">Emergency: 911</a> <a href="tel:1115" class="btn btn-sm btn-danger w-100 fw-bold">Emergency: 1115</a>
</div> </div>
</div> </div>
</nav> </nav>
@ -226,6 +230,10 @@
</div> </div>
<div class="d-flex align-items-center gap-4"> <div class="d-flex align-items-center gap-4">
<div id="location-status" class="d-none d-md-flex align-items-center text-secondary small cursor-pointer" style="cursor: pointer;" onclick="detectLocation()">
<i class="bi bi-geo-alt me-2"></i>
<span id="location-text">Detect Location</span>
</div>
<div class="d-none d-md-flex align-items-center text-secondary small"> <div class="d-none d-md-flex align-items-center text-secondary small">
<i class="bi bi-calendar3 me-2"></i> <i class="bi bi-calendar3 me-2"></i>
{{ current_time|date:"D, M d, Y" }} {{ current_time|date:"D, M d, Y" }}
@ -245,6 +253,16 @@
</div> </div>
<div class="p-4 p-md-5"> <div class="p-4 p-md-5">
{% if messages %}
{% for message in messages %}
<div class="alert alert-{{ message.tags }} alert-dismissible fade show shadow-sm border-0 mb-4" role="alert">
{% if message.tags == 'success' %}<i class="bi bi-check-circle-fill me-2"></i>{% endif %}
{% if message.tags == 'error' %}<i class="bi bi-exclamation-triangle-fill me-2"></i>{% endif %}
{{ message }}
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
{% endfor %}
{% endif %}
{% block content %}{% endblock %} {% block content %}{% endblock %}
</div> </div>
</div> </div>
@ -265,7 +283,51 @@
} }
}); });
// Active class is handled server-side via Django templates. function detectLocation() {
const text = document.getElementById('location-text');
const originalText = text.innerText;
text.innerText = "Locating...";
if ("geolocation" in navigator) {
navigator.geolocation.getCurrentPosition(function(position) {
const lat = position.coords.latitude;
const lng = position.coords.longitude;
text.innerText = lat.toFixed(4) + ", " + lng.toFixed(4);
// Save to cookies for 1 day
document.cookie = `user_lat=${lat}; path=/; max-age=86400`;
document.cookie = `user_lng=${lng}; path=/; max-age=86400`;
// If we are on a list page without lat/lng, reload with them
const urlParams = new URLSearchParams(window.location.search);
if (!urlParams.has('lat')) {
urlParams.set('lat', lat);
urlParams.set('lng', lng);
window.location.search = urlParams.toString();
}
}, function(error) {
text.innerText = "Error";
console.error(error);
});
} else {
text.innerText = "Not supported";
}
}
// Check if location is already in cookies
function checkLocationCookie() {
const cookies = document.cookie.split(';');
let lat = null, lng = null;
for (let cookie of cookies) {
const [name, value] = cookie.trim().split('=');
if (name === 'user_lat') lat = value;
if (name === 'user_lng') lng = value;
}
if (lat && lng) {
document.getElementById('location-text').innerText = parseFloat(lat).toFixed(4) + ", " + parseFloat(lng).toFixed(4);
}
}
checkLocationCookie();
</script> </script>
{% block scripts %}{% endblock %} {% block scripts %}{% endblock %}
</body> </body>

View File

@ -0,0 +1,74 @@
{% extends "base.html" %}
{% load static %}
{% block title %}Add Vaccination Record - {{ project_name }}{% endblock %}
{% block content %}
<div class="container py-5">
<div class="row justify-content-center">
<div class="col-lg-6">
<div class="glass-card shadow-lg p-4 p-md-5">
<div class="text-center mb-4">
<div class="icon-box bg-danger bg-opacity-10 text-danger rounded-circle d-inline-flex p-3 mb-3">
<i class="bi bi-shield-plus fs-1"></i>
</div>
<h2 class="brand-font text-dark">Add Vaccine Record</h2>
<p class="text-secondary">Keep your immunization data accurate and up to date.</p>
</div>
<form method="POST">
{% csrf_token %}
<div class="mb-3">
<label class="form-label text-dark fw-bold">Vaccine Name</label>
<select name="vaccine_name" class="form-select bg-light border-secondary text-dark" required>
<option value="">Select Vaccine...</option>
<option value="COVID-19 (Covishield/AstraZeneca)">COVID-19 (Covishield/AstraZeneca)</option>
<option value="COVID-19 (Vero Cell)">COVID-19 (Vero Cell)</option>
<option value="COVID-19 (Pfizer/BioNTech)">COVID-19 (Pfizer/BioNTech)</option>
<option value="COVID-19 (Moderna)">COVID-19 (Moderna)</option>
<option value="COVID-19 (Johnson & Johnson)">COVID-19 (Johnson & Johnson)</option>
<option value="Hepatitis B">Hepatitis B</option>
<option value="Influenza (Flu)">Influenza (Flu)</option>
<option value="HPV">HPV</option>
<option value="Tetanus">Tetanus</option>
<option value="Other">Other</option>
</select>
</div>
<div class="row">
<div class="col-md-6 mb-3">
<label class="form-label text-dark fw-bold">Dose Number</label>
<input type="number" name="dose_number" class="form-control bg-light border-secondary text-dark" min="1" value="1" required>
</div>
<div class="col-md-6 mb-3">
<label class="form-label text-dark fw-bold">Date Taken</label>
<input type="date" name="date_taken" class="form-control bg-light border-secondary text-dark" required>
</div>
</div>
<div class="mb-3">
<label class="form-label text-dark fw-bold">City/District</label>
<input type="text" name="location" class="form-control bg-light border-secondary text-dark" placeholder="e.g. Kathmandu" required>
</div>
<div class="mb-3">
<label class="form-label text-dark fw-bold">Center Name</label>
<input type="text" name="center_name" class="form-control bg-light border-secondary text-dark" placeholder="e.g. Teaching Hospital">
</div>
<div class="mb-4">
<label class="form-label text-dark fw-bold">Notes (Optional)</label>
<textarea name="notes" class="form-control bg-light border-secondary text-dark" rows="3" placeholder="Any side effects or remarks..."></textarea>
</div>
<div class="d-grid gap-2">
<button type="submit" class="btn btn-danger py-3 rounded-pill fw-bold shadow-sm">Save Record</button>
<a href="{% url 'vaccination_dashboard' %}" class="btn btn-light py-3 rounded-pill fw-bold">Cancel</a>
</div>
</form>
</div>
</div>
</div>
</div>
{% endblock %}

View File

@ -6,24 +6,47 @@
{% block content %} {% block content %}
<div class="container-fluid p-0"> <div class="container-fluid p-0">
<div class="row mb-4"> <div class="row mb-4">
<div class="col-12"> <div class="col-md-8">
<h2 class="brand-font mb-1">Blood Banks</h2> <h2 class="brand-font mb-1">Blood Banks</h2>
<p class="text-secondary">Official blood repositories and their current inventory levels.</p> <p class="text-secondary">Official blood repositories and their current inventory levels.</p>
</div> </div>
<div class="col-md-4 text-md-end">
<button type="button" id="findNearestBankBtn" class="btn btn-primary shadow-sm">
<i class="bi bi-geo-alt-fill me-1"></i> Nearest First
</button>
<a href="{% url 'blood_bank_list' %}" class="btn btn-outline-secondary ms-2">Reset</a>
<form id="bankFilterForm" method="GET" style="display:none;">
<input type="hidden" name="lat" id="latInputBank">
<input type="hidden" name="lng" id="lngInputBank">
</form>
</div>
</div> </div>
<div class="row"> <div class="row">
{% for bank in banks %} {% for bank in banks %}
<div class="col-md-6 mb-4"> <div class="col-md-6 mb-4">
<div class="glass-card h-100"> <div class="glass-card h-100">
<div class="d-flex justify-content-between align-items-center mb-4"> <div class="d-flex justify-content-between align-items-center mb-2">
<h4 class="brand-font mb-0 text-dark">{{ bank.name }}</h4> <h4 class="brand-font mb-0 text-dark">{{ bank.name }}</h4>
{% if bank.is_24_7 %} {% if bank.is_24_7 %}
<span class="badge bg-success bg-opacity-10 text-success">24/7 Available</span> <span class="badge bg-success bg-opacity-10 text-success">24/7 Available</span>
{% endif %} {% endif %}
</div> </div>
<p class="text-secondary mb-4"><i class="bi bi-geo-alt me-2"></i> {{ bank.location }}</p>
<p class="text-secondary mb-4"><i class="bi bi-telephone me-2"></i> {{ bank.contact_number }}</p> {% if bank.distance and bank.distance < 1000 %}
<div class="mb-3">
<span class="badge bg-info bg-opacity-10 text-info" style="font-size: 0.75rem;">
<i class="bi bi-distribute-vertical me-1"></i> {{ bank.distance|floatformat:1 }} km away from you
</span>
</div>
{% endif %}
<p class="text-secondary mb-3"><i class="bi bi-geo-alt me-2"></i> {{ bank.location }}</p>
<p class="text-secondary mb-4">
<a href="tel:{{ bank.contact_number }}" class="text-decoration-none text-secondary">
<i class="bi bi-telephone me-2"></i> {{ bank.contact_number }}
</a>
</p>
<h6 class="fw-bold text-dark mb-3">Inventory Levels (Units)</h6> <h6 class="fw-bold text-dark mb-3">Inventory Levels (Units)</h6>
<div class="row g-2 mb-4"> <div class="row g-2 mb-4">
@ -77,7 +100,7 @@
</div> </div>
</div> </div>
<button class="btn btn-danger w-100 rounded-pill">Contact Bank</button> <a href="tel:{{ bank.contact_number }}" class="btn btn-danger w-100 rounded-pill">Contact Bank</a>
</div> </div>
</div> </div>
{% empty %} {% empty %}
@ -89,3 +112,30 @@
</div> </div>
</div> </div>
{% endblock %} {% endblock %}
{% block scripts %}
<script>
document.getElementById('findNearestBankBtn').addEventListener('click', function() {
const btn = this;
const originalContent = btn.innerHTML;
btn.disabled = true;
btn.innerHTML = '<span class="spinner-border spinner-border-sm me-1"></span> Locating...';
if ("geolocation" in navigator) {
navigator.geolocation.getCurrentPosition(function(position) {
document.getElementById('latInputBank').value = position.coords.latitude;
document.getElementById('lngInputBank').value = position.coords.longitude;
document.getElementById('bankFilterForm').submit();
}, function(error) {
alert("Error getting location: " + error.message);
btn.disabled = false;
btn.innerHTML = originalContent;
});
} else {
alert("Geolocation is not supported by this browser.");
btn.disabled = false;
btn.innerHTML = originalContent;
}
});
</script>
{% endblock %}

View File

@ -24,7 +24,7 @@
<h2 class="brand-font mb-1">Blood Requests</h2> <h2 class="brand-font mb-1">Blood Requests</h2>
<p class="text-secondary">Current active requirements for blood in various hospitals.</p> <p class="text-secondary">Current active requirements for blood in various hospitals.</p>
</div> </div>
<button class="btn btn-danger rounded-pill px-4">Post a Request</button> <a href="{% url 'request_blood' %}" class="btn btn-danger rounded-pill px-4">Post a Request</a>
</div> </div>
</div> </div>

View File

@ -70,10 +70,13 @@
</span> </span>
<p class="mb-0 text-muted extra-small" style="font-size: 0.7rem;">Phone: {{ donor.phone }}</p> <p class="mb-0 text-muted extra-small" style="font-size: 0.7rem;">Phone: {{ donor.phone }}</p>
</div> </div>
<button class="btn btn-outline-danger btn-sm px-3 rounded-pill" onclick="alert('Contacting {{ donor.name }} at {{ donor.phone }}...')">Contact Now</button> <a href="tel:{{ donor.phone }}" class="btn btn-outline-danger btn-sm px-3 rounded-pill">Contact Now</a>
</div> </div>
</div> {% endfor %}
{% endblock %} </div>
</div>
</div>
{% endblock %}
{% block scripts %} {% block scripts %}
<script> <script>

View File

@ -212,7 +212,7 @@
</span> </span>
<p class="mb-0 text-muted extra-small" style="font-size: 0.7rem;">Last Donated: {{ donor.last_donation_date|default:"Never" }}</p> <p class="mb-0 text-muted extra-small" style="font-size: 0.7rem;">Last Donated: {{ donor.last_donation_date|default:"Never" }}</p>
</div> </div>
<button class="btn btn-outline-danger btn-sm px-3 rounded-pill" onclick="alert('Initiating contact with {{ donor.name }}...')">Contact</button> <a href="tel:{{ donor.phone }}" class="btn btn-outline-danger btn-sm px-3 rounded-pill">Contact</a>
</div> </div>
</div> </div>
{% empty %} {% empty %}
@ -239,7 +239,7 @@
<div class="progress-bar bg-success" style="width: {{ stats.vaccinated_percentage }}%"></div> <div class="progress-bar bg-success" style="width: {{ stats.vaccinated_percentage }}%"></div>
</div> </div>
</div> </div>
<button class="btn btn-success btn-sm px-4">Update Status</button> <a href="{% url 'vaccination_dashboard' %}" class="btn btn-success btn-sm px-4">Update Status</a>
</div> </div>
<div class="col-md-5 d-none d-md-block text-center"> <div class="col-md-5 d-none d-md-block text-center">
<i class="bi bi-shield-plus text-success" style="font-size: 5rem; opacity: 0.2;"></i> <i class="bi bi-shield-plus text-success" style="font-size: 5rem; opacity: 0.2;"></i>
@ -267,7 +267,7 @@
<p class="text-secondary extra-small mb-2"><i class="bi bi-hospital me-1"></i> {{ req.hospital }}</p> <p class="text-secondary extra-small mb-2"><i class="bi bi-hospital me-1"></i> {{ req.hospital }}</p>
<div class="d-flex justify-content-between align-items-center"> <div class="d-flex justify-content-between align-items-center">
<small class="text-muted" style="font-size: 0.7rem;">{{ req.created_at|timesince }} ago</small> <small class="text-muted" style="font-size: 0.7rem;">{{ req.created_at|timesince }} ago</small>
<a href="#" class="btn btn-link text-danger p-0 text-decoration-none small">Help Now →</a> <a href="tel:{{ req.contact_number }}" class="btn btn-link text-danger p-0 text-decoration-none small">Help Now →</a>
</div> </div>
</div> </div>
{% empty %} {% empty %}

View File

@ -0,0 +1,134 @@
{% extends "base.html" %}
{% block title %}{{ title }}{% endblock %}
{% block content %}
<div class="container mt-4">
<div class="d-flex justify-content-between align-items-center mb-4">
<h2><i class="bi bi-map text-danger"></i> Live Alert Map</h2>
<span class="badge bg-danger pulse">LIVE UPDATES</span>
</div>
<div class="card shadow-sm mb-4">
<div class="card-body p-0">
<div id="live-map" style="height: 600px; width: 100%; border-radius: 8px;"></div>
</div>
</div>
<div class="row">
<div class="col-md-12">
<div class="card shadow-sm">
<div class="card-header bg-white">
<h5 class="mb-0">Recent Alerts</h5>
</div>
<div class="card-body">
<div class="table-responsive">
<table class="table table-hover">
<thead>
<tr>
<th>Patient</th>
<th>Blood Group</th>
<th>Hospital</th>
<th>Urgency</th>
<th>Action</th>
</tr>
</thead>
<tbody>
{% for req in requests %}
<tr>
<td>{{ req.patient_name }}</td>
<td><span class="badge bg-danger">{{ req.blood_group }}</span></td>
<td>{{ req.hospital }}</td>
<td>
{% if req.urgency == 'CRITICAL' %}
<span class="badge bg-dark">CRITICAL</span>
{% elif req.urgency == 'URGENT' %}
<span class="badge bg-warning text-dark">URGENT</span>
{% else %}
<span class="badge bg-info">NORMAL</span>
{% endif %}
</td>
<td>
<button class="btn btn-sm btn-outline-danger" onclick="focusOnMarker({{ req.latitude }}, {{ req.longitude }})">
<i class="bi bi-eye"></i> View on Map
</button>
</td>
</tr>
{% empty %}
<tr>
<td colspan="5" class="text-center">No active alerts found.</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
<style>
.pulse {
animation: pulse-red 2s infinite;
}
@keyframes pulse-red {
0% { transform: scale(0.95); box-shadow: 0 0 0 0 rgba(220, 53, 69, 0.7); }
70% { transform: scale(1); box-shadow: 0 0 0 10px rgba(220, 53, 69, 0); }
100% { transform: scale(0.95); box-shadow: 0 0 0 0 rgba(220, 53, 69, 0); }
}
</style>
<script>
var map;
var markers = {};
function initMap() {
// Center on Kathmandu by default
map = L.map('live-map').setView([27.7172, 85.3240], 13);
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '© OpenStreetMap contributors'
}).addTo(map);
{% for req in requests %}
{% if req.latitude and req.longitude %}
var markerColor = 'blue';
{% if req.urgency == 'CRITICAL' %}
markerColor = 'red';
{% elif req.urgency == 'URGENT' %}
markerColor = 'orange';
{% endif %}
var marker = L.circleMarker([{{ req.latitude }}, {{ req.longitude }}], {
radius: 10,
fillColor: markerColor,
color: "#fff",
weight: 1,
opacity: 1,
fillOpacity: 0.8
}).addTo(map);
marker.bindPopup(`
<strong>{{ req.patient_name }}</strong><br>
Blood Group: <span class="badge bg-danger">{{ req.blood_group }}</span><br>
Hospital: {{ req.hospital }}<br>
Urgency: {{ req.urgency }}<br>
<a href="tel:{{ req.contact_number }}" class="btn btn-sm btn-primary mt-2">Contact</a>
`);
markers["{{ req.id }}"] = marker;
{% endif %}
{% endfor %}
}
function focusOnMarker(lat, lng) {
map.setView([lat, lng], 16);
// Find marker at this location and open its popup if possible
// (Simplified for now)
window.scrollTo({ top: 0, behavior: 'smooth' });
}
document.addEventListener('DOMContentLoaded', initMap);
</script>
{% endblock %}

View File

@ -3,58 +3,97 @@
{% block title %}Login - RaktaPulse{% endblock %} {% block title %}Login - RaktaPulse{% endblock %}
{% block content %} {% block content %}
<div class="row justify-content-center py-5"> <div class="row justify-content-center align-items-center py-5" style="min-height: 80vh;">
<div class="col-md-5"> <div class="col-md-5">
<div class="glass-card shadow-sm border-danger border-opacity-10"> <div class="glass-card shadow-lg border-danger border-opacity-10 p-4 p-md-5">
<div class="text-center mb-4"> <div class="text-center mb-5">
<div class="bg-danger rounded-circle d-inline-flex align-items-center justify-content-center mb-3 blinking-logo" style="width: 60px; height: 60px;"> <div class="bg-danger rounded-circle d-inline-flex align-items-center justify-content-center mb-4 blinking-logo shadow-danger" style="width: 70px; height: 70px; box-shadow: 0 0 20px rgba(230, 57, 70, 0.3);">
<i class="bi bi-droplet-fill text-white fs-2"></i> <i class="bi bi-droplet-fill text-white fs-1"></i>
</div> </div>
<h2 class="fw-bold text-danger brand-font">Welcome to RaktaPulse</h2> <h2 class="fw-bold text-danger brand-font mb-2">Welcome Back</h2>
<p class="text-secondary">Please login to your account</p> <p class="text-secondary">Enter your credentials to access RaktaPulse</p>
</div> </div>
<form method="post"> {% if form.non_field_errors %}
<div class="alert alert-danger border-0 rounded-3 mb-4 small">
{% for error in form.non_field_errors %}
<div class="d-flex align-items-center">
<i class="bi bi-exclamation-triangle-fill me-2"></i>
{{ error }}
</div>
{% endfor %}
</div>
{% endif %}
<form method="post" class="needs-validation">
{% csrf_token %} {% csrf_token %}
{% for field in form %} {% for field in form %}
<div class="mb-3"> <div class="mb-4">
<label class="form-label small fw-bold text-secondary text-uppercase">{{ field.label }}</label> <label class="form-label small fw-bold text-secondary text-uppercase mb-2" for="{{ field.id_for_label }}">
<i class="bi bi-{% if 'username' in field.name %}person{% else %}key{% endif %} me-1"></i> {{ field.label }}
</label>
{{ field }} {{ field }}
{% if field.errors %} {% if field.errors %}
<div class="text-danger small mt-1">{{ field.errors.0 }}</div> <div class="text-danger small mt-1">
{% for error in field.errors %}{{ error }}{% endfor %}
</div>
{% endif %} {% endif %}
</div> </div>
{% endfor %} {% endfor %}
<button type="submit" class="btn btn-danger w-100 py-2 fw-bold mt-3">LOGIN</button> <div class="d-flex justify-content-between align-items-center mb-4">
<div class="form-check">
<input class="form-check-input" type="checkbox" id="rememberMe">
<label class="form-check-label small text-secondary" for="rememberMe">Remember me</label>
</div>
<a href="#" class="text-danger small text-decoration-none fw-500">Forgot password?</a>
</div>
<button type="submit" class="btn btn-danger w-100 py-3 fw-bold mt-2 shadow-sm">
LOG IN <i class="bi bi-arrow-right-short ms-1"></i>
</button>
</form> </form>
<div class="text-center mt-4"> <div class="text-center mt-5">
<p class="text-secondary small">Don't have an account? <a href="{% url 'register' %}" class="text-danger fw-bold text-decoration-none">Register here</a></p> <p class="text-secondary small mb-4">Don't have an account? <a href="{% url 'register' %}" class="text-danger fw-bold text-decoration-none">Create one now</a></p>
<hr class="opacity-10 mb-4">
<a href="{% url 'home' %}" class="btn btn-link btn-sm text-secondary text-decoration-none">
<i class="bi bi-house-door me-1"></i> Back to Home
</a>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<style> <style>
input { input:not([type="checkbox"]) {
display: block; display: block;
width: 100%; width: 100%;
padding: 0.75rem 1rem; padding: 0.85rem 1.25rem;
font-size: 1rem; font-size: 1rem;
font-weight: 400; font-weight: 400;
line-height: 1.5; line-height: 1.5;
color: #212529; color: #2b2d42;
background-color: #f8f9fa; background-color: #ffffff;
background-clip: padding-box; background-clip: padding-box;
border: 1px solid #dee2e6; border: 1px solid #e9ecef;
border-radius: 8px; border-radius: 12px;
transition: border-color .15s ease-in-out,box-shadow .15s ease-in-out; transition: all 0.2s ease-in-out;
} }
input:focus { input:not([type="checkbox"]):focus {
border-color: #E63946; border-color: #E63946;
outline: 0; outline: 0;
box-shadow: 0 0 0 0.25rem rgba(230, 57, 70, 0.1); box-shadow: 0 0 0 4px rgba(230, 57, 70, 0.08);
background-color: #fff;
}
.glass-card {
background: #ffffff;
border-radius: 24px;
border: 1px solid rgba(0,0,0,0.05);
}
.fw-500 { font-weight: 500; }
.shadow-danger {
box-shadow: 0 10px 20px rgba(230, 57, 70, 0.2);
} }
</style> </style>
{% endblock %} {% endblock %}

View File

@ -0,0 +1,89 @@
{% extends 'base.html' %}
{% block title %}Create Account - RaktaPulse{% endblock %}
{% block content %}
<div class="row justify-content-center align-items-center py-5" style="min-height: 80vh;">
<div class="col-md-6 col-lg-5">
<div class="glass-card shadow-lg border-danger border-opacity-10 p-4 p-md-5">
<div class="text-center mb-5">
<div class="bg-danger rounded-circle d-inline-flex align-items-center justify-content-center mb-4 blinking-logo shadow-danger" style="width: 70px; height: 70px; box-shadow: 0 0 20px rgba(230, 57, 70, 0.3);">
<i class="bi bi-person-plus-fill text-white fs-1"></i>
</div>
<h2 class="fw-bold text-danger brand-font mb-2">Join RaktaPulse</h2>
<p class="text-secondary">Be a hero. Start saving lives today.</p>
</div>
<form method="post" class="needs-validation">
{% csrf_token %}
{% for field in form %}
<div class="mb-3">
<label class="form-label small fw-bold text-secondary text-uppercase mb-2" for="{{ field.id_for_label }}">
{{ field.label }}
</label>
{{ field }}
{% if field.help_text %}
<div class="form-text small mb-1">{{ field.help_text|safe }}</div>
{% endif %}
{% if field.errors %}
<div class="text-danger small mt-1">
{% for error in field.errors %}{{ error }}{% endfor %}
</div>
{% endif %}
</div>
{% endfor %}
<button type="submit" class="btn btn-danger w-100 py-3 fw-bold mt-4 shadow-sm">
CREATE ACCOUNT <i class="bi bi-check-circle ms-1"></i>
</button>
</form>
<div class="text-center mt-5">
<p class="text-secondary small mb-4">Already have an account? <a href="{% url 'login' %}" class="text-danger fw-bold text-decoration-none">Log in here</a></p>
<hr class="opacity-10 mb-4">
<a href="{% url 'home' %}" class="btn btn-link btn-sm text-secondary text-decoration-none">
<i class="bi bi-house-door me-1"></i> Back to Home
</a>
</div>
</div>
</div>
</div>
<style>
input:not([type="checkbox"]) {
display: block;
width: 100%;
padding: 0.85rem 1.25rem;
font-size: 1rem;
font-weight: 400;
line-height: 1.5;
color: #2b2d42;
background-color: #ffffff;
background-clip: padding-box;
border: 1px solid #e9ecef;
border-radius: 12px;
transition: all 0.2s ease-in-out;
}
input:not([type="checkbox"]):focus {
border-color: #E63946;
outline: 0;
box-shadow: 0 0 0 4px rgba(230, 57, 70, 0.08);
background-color: #fff;
}
.glass-card {
background: #ffffff;
border-radius: 24px;
border: 1px solid rgba(0,0,0,0.05);
}
.shadow-danger {
box-shadow: 0 10px 20px rgba(230, 57, 70, 0.2);
}
.helptext ul {
list-style: none;
padding-left: 0;
margin-top: 5px;
color: #6c757d;
font-size: 0.8rem;
}
</style>
{% endblock %}

View File

@ -0,0 +1,99 @@
{% extends "base.html" %}
{% block title %}Post Blood Request - RaktaPulse{% endblock %}
{% block content %}
<div class="container mt-4">
<div class="row justify-content-center">
<div class="col-md-8">
<div class="card shadow-sm border-0 rounded-4">
<div class="card-body p-4 p-md-5">
<div class="text-center mb-4">
<div class="bg-danger bg-opacity-10 text-danger rounded-circle d-inline-flex align-items-center justify-content-center mb-3" style="width: 60px; height: 60px;">
<i class="bi bi-megaphone-fill fs-3"></i>
</div>
<h2 class="fw-bold">Post a Blood Request</h2>
<p class="text-secondary">Fill in the details to alert nearby donors.</p>
</div>
<form method="POST" id="requestForm">
{% csrf_token %}
<div class="row g-3">
<div class="col-md-6">
<label class="form-label small fw-bold text-secondary">Patient Name *</label>
<input type="text" name="patient_name" class="form-control rounded-3" required>
</div>
<div class="col-md-6">
<label class="form-label small fw-bold text-secondary">Blood Group *</label>
<select name="blood_group" class="form-select rounded-3" required>
<option value="" selected disabled>Select Group</option>
{% for group in blood_groups %}
<option value="{{ group }}">{{ group }}</option>
{% endfor %}
</select>
</div>
<div class="col-md-6">
<label class="form-label small fw-bold text-secondary">Hospital Name *</label>
<input type="text" name="hospital" class="form-control rounded-3" required>
</div>
<div class="col-md-6">
<label class="form-label small fw-bold text-secondary">Urgency *</label>
<select name="urgency" class="form-select rounded-3">
{% for val, label in urgency_levels %}
<option value="{{ val }}">{{ label }}</option>
{% endfor %}
</select>
</div>
<div class="col-12">
<label class="form-label small fw-bold text-secondary">Location (City/Area) *</label>
<input type="text" name="location" class="form-control rounded-3" placeholder="e.g. Balaju, Kathmandu" required>
</div>
<div class="col-12">
<label class="form-label small fw-bold text-secondary">Contact Number *</label>
<input type="text" name="contact_number" class="form-control rounded-3" required>
</div>
<!-- Hidden coordinates -->
<input type="hidden" name="latitude" id="id_latitude">
<input type="hidden" name="longitude" id="id_longitude">
</div>
<div class="alert alert-info mt-4 d-flex align-items-center small" id="geo-status">
<i class="bi bi-geo-alt-fill me-2"></i>
<span>Fetching your precise location for the live map...</span>
</div>
<div class="mt-4">
<button type="submit" class="btn btn-danger w-100 py-3 rounded-3 fw-bold shadow-sm">
<i class="bi bi-send-fill me-2"></i> Broadcast Alert
</button>
<a href="{% url 'blood_request_list' %}" class="btn btn-link w-100 mt-2 text-secondary text-decoration-none small">Cancel</a>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
if ("geolocation" in navigator) {
navigator.geolocation.getCurrentPosition(function(position) {
document.getElementById('id_latitude').value = position.coords.latitude;
document.getElementById('id_longitude').value = position.coords.longitude;
const status = document.getElementById('geo-status');
status.classList.replace('alert-info', 'alert-success');
status.innerHTML = '<i class="bi bi-check-circle-fill me-2"></i> Precise location captured! This will appear on the live map.';
}, function(error) {
const status = document.getElementById('geo-status');
status.classList.replace('alert-info', 'alert-warning');
status.innerHTML = '<i class="bi bi-exclamation-triangle-fill me-2"></i> Could not get precise location. You can still post, but it won\'t show on the live map.';
console.warn("Geolocation error:", error);
});
} else {
document.getElementById('geo-status').style.display = 'none';
}
});
</script>
{% endblock %}

View File

@ -0,0 +1,103 @@
{% extends "base.html" %}
{% load static %}
{% block title %}My Vaccination Records - {{ project_name }}{% endblock %}
{% block head %}
<style>
.vaccine-card {
background: #ffffff;
border-radius: 15px;
padding: 20px;
border: 1px solid var(--border-color);
transition: transform 0.2s, box-shadow 0.2s;
margin-bottom: 20px;
}
.vaccine-card:hover {
transform: translateY(-5px);
box-shadow: 0 10px 20px rgba(0,0,0,0.05);
}
.dose-badge {
background: var(--pulse-red-light);
color: var(--pulse-red);
font-weight: 700;
padding: 5px 12px;
border-radius: 8px;
font-size: 0.8rem;
}
.date-badge {
background: #f8f9fa;
color: #6c757d;
padding: 5px 12px;
border-radius: 8px;
font-size: 0.8rem;
display: flex;
align-items: center;
gap: 5px;
}
</style>
{% endblock %}
{% block content %}
<div class="container py-4">
<div class="d-flex justify-content-between align-items-center mb-4">
<div>
<h2 class="brand-font mb-1 text-dark">Vaccination Journey</h2>
<p class="text-secondary">Track and manage your immunization history safely.</p>
</div>
<a href="{% url 'add_vaccination' %}" class="btn btn-danger rounded-pill px-4 shadow-sm">
<i class="bi bi-plus-lg me-2"></i>Add New Record
</a>
</div>
{% if messages %}
<div class="mb-4">
{% for message in messages %}
<div class="alert alert-{{ message.tags }} alert-dismissible fade show rounded-4 shadow-sm border-0" role="alert">
<i class="bi {% if message.tags == 'success' %}bi-check-circle-fill{% else %}bi-exclamation-triangle-fill{% endif %} me-2"></i>
{{ message }}
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
{% endfor %}
</div>
{% endif %}
<div class="row">
{% for record in records %}
<div class="col-md-6 col-lg-4">
<div class="vaccine-card shadow-sm">
<div class="d-flex justify-content-between align-items-start mb-3">
<h5 class="fw-bold mb-0 text-dark">{{ record.vaccine_name }}</h5>
<span class="dose-badge">Dose {{ record.dose_number }}</span>
</div>
<div class="d-flex flex-wrap gap-2 mb-3">
<span class="date-badge"><i class="bi bi-calendar-event"></i> {{ record.date_taken|date:"M d, Y" }}</span>
<span class="date-badge"><i class="bi bi-geo-alt"></i> {{ record.center_name|default:record.location }}</span>
</div>
<div class="mt-2 border-top pt-2">
<p class="text-secondary small mb-0">
<strong>Location:</strong> {{ record.location }}
</p>
{% if record.notes %}
<p class="text-secondary small mt-2 mb-0">
<i class="bi bi-sticky me-1"></i> {{ record.notes|truncatechars:100 }}
</p>
{% endif %}
</div>
</div>
</div>
{% empty %}
<div class="col-12 text-center py-5">
<div class="glass-card py-5">
<i class="bi bi-shield-slash fs-1 text-secondary opacity-25"></i>
<h4 class="mt-3 text-dark">No records found</h4>
<p class="text-secondary">Start tracking your vaccinations today to keep your community safe.</p>
<a href="{% url 'add_vaccination' %}" class="btn btn-outline-danger mt-2 rounded-pill px-4">Add Your First Record</a>
</div>
</div>
{% endfor %}
</div>
</div>
{% endblock %}

View File

@ -1,6 +1,6 @@
from django.urls import path from django.urls import path
from .views import home, login_view, logout_view, register_view, donor_list, blood_request_list, blood_bank_list, vaccination_info from .views import home, login_view, logout_view, register_view, donor_list, blood_request_list, blood_bank_list, vaccination_info, vaccination_dashboard, add_vaccination, live_map
urlpatterns = [ urlpatterns = [
path("", home, name="home"), path("", home, name="home"),
@ -11,4 +11,8 @@ urlpatterns = [
path("requests/", blood_request_list, name="blood_request_list"), path("requests/", blood_request_list, name="blood_request_list"),
path("banks/", blood_bank_list, name="blood_bank_list"), path("banks/", blood_bank_list, name="blood_bank_list"),
path("vaccination/", vaccination_info, name="vaccination_info"), path("vaccination/", vaccination_info, name="vaccination_info"),
path("vaccination/dashboard/", vaccination_dashboard, name="vaccination_dashboard"),
path("vaccination/add/", add_vaccination, name="add_vaccination"),
path("live-map/", live_map, name="live_map"),
path("request-blood/", request_blood, name="request_blood"),
] ]

View File

@ -3,9 +3,10 @@ import platform
from django.shortcuts import render, redirect from django.shortcuts import render, redirect
from django.contrib.auth import login, logout, authenticate from django.contrib.auth import login, logout, authenticate
from django.contrib.auth.forms import UserCreationForm, AuthenticationForm from django.contrib.auth.forms import UserCreationForm, AuthenticationForm
from django.contrib.auth.decorators import login_required
from django.contrib import messages from django.contrib import messages
from django.utils import timezone from django.utils import timezone
from .models import Donor, BloodRequest, BloodBank from .models import Donor, BloodRequest, BloodBank, VaccineRecord
import math import math
def haversine(lat1, lon1, lat2, lon2): def haversine(lat1, lon1, lat2, lon2):
@ -24,12 +25,12 @@ def login_view(request):
if request.method == "POST": if request.method == "POST":
form = AuthenticationForm(request, data=request.POST) form = AuthenticationForm(request, data=request.POST)
if form.is_valid(): if form.is_valid():
username = form.cleaned_data.get('username') user = form.get_user()
password = form.cleaned_data.get('password') login(request, user)
user = authenticate(username=username, password=password) messages.success(request, f"Welcome back, {user.username}!")
if user is not None: return redirect("home")
login(request, user) else:
return redirect("home") messages.error(request, "Invalid username or password. Please try again.")
else: else:
form = AuthenticationForm() form = AuthenticationForm()
return render(request, "core/login.html", {"form": form}) return render(request, "core/login.html", {"form": form})
@ -153,9 +154,27 @@ def blood_request_list(request):
return render(request, 'core/blood_request_list.html', context) return render(request, 'core/blood_request_list.html', context)
def blood_bank_list(request): def blood_bank_list(request):
user_lat = request.GET.get('lat')
user_lng = request.GET.get('lng')
banks = BloodBank.objects.all() banks = BloodBank.objects.all()
bank_list_data = list(banks)
if user_lat and user_lng:
try:
u_lat = float(user_lat)
u_lng = float(user_lng)
for b in bank_list_data:
if b.latitude and b.longitude:
b.distance = haversine(u_lat, u_lng, float(b.latitude), float(b.longitude))
else:
b.distance = 999999
bank_list_data.sort(key=lambda x: x.distance)
except ValueError:
pass
context = { context = {
'banks': banks, 'banks': bank_list_data,
} }
return render(request, 'core/blood_bank_list.html', context) return render(request, 'core/blood_bank_list.html', context)
@ -170,3 +189,85 @@ def vaccination_info(request):
stats["percentage"] = 0 stats["percentage"] = 0
return render(request, 'core/vaccination_info.html', {'stats': stats}) return render(request, 'core/vaccination_info.html', {'stats': stats})
def live_map(request):
"""View to display live alerts/requests on a map."""
active_requests = BloodRequest.objects.filter(status='Active').order_by('-created_at')
# Also include blood banks and donors optionally if we want a full map
# But focusing on alerts as requested.
context = {
'requests': active_requests,
'title': 'Live Alert Map',
}
return render(request, 'core/live_map.html', context)
def request_blood(request):
"""View to create a new blood request with geolocation."""
if request.method == "POST":
patient_name = request.POST.get('patient_name')
blood_group = request.POST.get('blood_group')
location = request.POST.get('location')
urgency = request.POST.get('urgency')
hospital = request.POST.get('hospital')
contact_number = request.POST.get('contact_number')
latitude = request.POST.get('latitude')
longitude = request.POST.get('longitude')
if patient_name and blood_group and hospital and contact_number:
BloodRequest.objects.create(
patient_name=patient_name,
blood_group=blood_group,
location=location,
urgency=urgency,
hospital=hospital,
contact_number=contact_number,
latitude=latitude if latitude else None,
longitude=longitude if longitude else None
)
messages.success(request, "Blood request posted successfully! Help is on the way.")
return redirect('blood_request_list')
else:
messages.error(request, "Please fill in all required fields.")
context = {
'blood_groups': [g[0] for g in Donor.BLOOD_GROUPS],
'urgency_levels': BloodRequest.URGENCY_LEVELS,
}
return render(request, 'core/request_blood.html', context)
@login_required
def vaccination_dashboard(request):
records = VaccineRecord.objects.filter(user=request.user).order_by('-date_taken')
context = {
'records': records,
'project_name': "RaktaPulse",
}
return render(request, 'core/vaccination_dashboard.html', context)
@login_required
def add_vaccination(request):
if request.method == "POST":
vaccine_name = request.POST.get('vaccine_name')
dose_number = request.POST.get('dose_number')
date_taken = request.POST.get('date_taken')
location = request.POST.get('location')
center_name = request.POST.get('center_name')
notes = request.POST.get('notes')
if vaccine_name and dose_number and date_taken:
VaccineRecord.objects.create(
user=request.user,
vaccine_name=vaccine_name,
dose_number=dose_number,
date_taken=date_taken,
location=location,
center_name=center_name,
notes=notes
)
messages.success(request, "Vaccination record added successfully!")
return redirect('vaccination_dashboard')
else:
messages.error(request, "Please fill in all required fields.")
return render(request, 'core/add_vaccination.html')

25
populate_coords.py Normal file
View File

@ -0,0 +1,25 @@
import os
import django
import random
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'config.settings')
django.setup()
from core.models import Donor, BloodBank
# Kathmandu center approx: 27.7172, 85.3240
def update_coords():
for donor in Donor.objects.all():
donor.latitude = 27.7172 + (random.random() - 0.5) * 0.1
donor.longitude = 85.3240 + (random.random() - 0.5) * 0.1
donor.save()
for bank in BloodBank.objects.all():
bank.latitude = 27.7172 + (random.random() - 0.5) * 0.1
bank.longitude = 85.3240 + (random.random() - 0.5) * 0.1
bank.save()
print("Updated coordinates for donors and banks.")
if __name__ == "__main__":
update_coords()