Autosave: 20260227-133224
This commit is contained in:
parent
24205f808f
commit
34ef272e25
Binary file not shown.
Binary file not shown.
@ -1,5 +1,6 @@
|
||||
from django.core.management.base import BaseCommand
|
||||
from django.utils import timezone
|
||||
from django.db.models import Q
|
||||
from core.models import BloodRequest
|
||||
from datetime import timedelta
|
||||
|
||||
@ -31,15 +32,29 @@ class Command(BaseCommand):
|
||||
self.stdout.write(self.style.SUCCESS(f'Deleted {count} {urgency} requests older than {days} days.'))
|
||||
deleted_count += count
|
||||
|
||||
# 2. Delete non-Active requests older than 7 days
|
||||
# 2. Delete non-Active and non-Accepted requests older than 7 days
|
||||
cutoff_inactive = now - timedelta(days=7)
|
||||
inactive_requests = BloodRequest.objects.exclude(status='Active').filter(created_at__lt=cutoff_inactive)
|
||||
inactive_requests = BloodRequest.objects.exclude(
|
||||
Q(status='Active') | Q(status='Accepted')
|
||||
).filter(created_at__lt=cutoff_inactive)
|
||||
count_inactive = inactive_requests.count()
|
||||
if count_inactive > 0:
|
||||
inactive_requests.delete()
|
||||
self.stdout.write(self.style.SUCCESS(f'Deleted {count_inactive} non-active requests older than 7 days.'))
|
||||
self.stdout.write(self.style.SUCCESS(f'Deleted {count_inactive} non-active/non-accepted requests older than 7 days.'))
|
||||
deleted_count += count_inactive
|
||||
|
||||
# 3. For Accepted requests, we keep them for 30 days after acceptance for history
|
||||
cutoff_accepted = now - timedelta(days=30)
|
||||
old_accepted = BloodRequest.objects.filter(
|
||||
status='Accepted',
|
||||
accepted_at__lt=cutoff_accepted
|
||||
)
|
||||
count_accepted = old_accepted.count()
|
||||
if count_accepted > 0:
|
||||
old_accepted.delete()
|
||||
self.stdout.write(self.style.SUCCESS(f'Deleted {count_accepted} accepted requests older than 30 days.'))
|
||||
deleted_count += count_accepted
|
||||
|
||||
if deleted_count == 0:
|
||||
self.stdout.write(self.style.SUCCESS('No old requests to delete.'))
|
||||
else:
|
||||
|
||||
18
core/migrations/0021_bloodrequest_accepted_at.py
Normal file
18
core/migrations/0021_bloodrequest_accepted_at.py
Normal file
@ -0,0 +1,18 @@
|
||||
# Generated by Django 5.2.7 on 2026-02-27 13:12
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('core', '0020_vaccinerecord_photo'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='bloodrequest',
|
||||
name='accepted_at',
|
||||
field=models.DateTimeField(blank=True, null=True),
|
||||
),
|
||||
]
|
||||
Binary file not shown.
@ -137,6 +137,7 @@ class BloodRequest(models.Model):
|
||||
image = models.ImageField(upload_to='blood_requests/', null=True, blank=True)
|
||||
required_date = models.DateField(default=timezone.now)
|
||||
status = models.CharField(max_length=20, default='Active')
|
||||
accepted_at = models.DateTimeField(null=True, blank=True)
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
|
||||
def __str__(self):
|
||||
|
||||
@ -80,6 +80,94 @@
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<h5 class="fw-bold mb-3 border-bottom pb-2">
|
||||
<i class="bi bi-clock-history me-2 text-danger"></i>Transaction & Operation History
|
||||
</h5>
|
||||
|
||||
<div class="mb-5">
|
||||
<h6 class="fw-bold text-muted small mb-3">Donor Record (People You've Helped)</h6>
|
||||
<div class="stats-mini bg-light p-3 rounded mb-3">
|
||||
<div class="row text-center">
|
||||
<div class="col-6 border-end">
|
||||
<div class="h4 mb-0 fw-bold text-danger">{{ donations_made.count }}</div>
|
||||
<div class="small text-muted">Total Volunteers</div>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<div class="h4 mb-0 fw-bold text-success">{{ completed_donations_count }}</div>
|
||||
<div class="small text-muted">Successful Donations</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if donations_made %}
|
||||
<div class="list-group list-group-flush border rounded overflow-hidden">
|
||||
{% for donation in donations_made %}
|
||||
<div class="list-group-item">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<div>
|
||||
<span class="fw-bold">Helped {{ donation.request.patient_name }}</span>
|
||||
<div class="small text-muted">{{ donation.request.hospital }} | {{ donation.date|date:"M d, Y" }}</div>
|
||||
</div>
|
||||
<div>
|
||||
{% if donation.is_completed %}
|
||||
<span class="badge bg-success">Completed</span>
|
||||
{% else %}
|
||||
<span class="badge bg-warning text-dark">Pending</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="text-center p-4 bg-light rounded border border-dashed">
|
||||
<p class="text-muted small mb-0">You haven't volunteered for any requests yet.</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="mb-5">
|
||||
<h6 class="fw-bold text-muted small mb-3">Request History (Who Helped You)</h6>
|
||||
{% if requests_made %}
|
||||
<div class="list-group list-group-flush border rounded overflow-hidden">
|
||||
{% for br in requests_made %}
|
||||
<div class="list-group-item">
|
||||
<div class="d-flex justify-content-between align-items-start">
|
||||
<div>
|
||||
<span class="fw-bold">{{ br.blood_group }} for {{ br.patient_name }}</span>
|
||||
<div class="small text-muted">{{ br.created_at|date:"M d, Y" }} | Status: <strong>{{ br.status }}</strong></div>
|
||||
|
||||
<div class="mt-2">
|
||||
<p class="small fw-bold mb-1">Volunteers:</p>
|
||||
{% with br.donations.all as donations %}
|
||||
{% if donations %}
|
||||
<div class="d-flex flex-wrap gap-2">
|
||||
{% for d in donations %}
|
||||
<a href="{% url 'public_profile' d.donor_user.username %}" class="btn btn-sm btn-outline-danger py-0 px-2 rounded-pill small">
|
||||
<i class="bi bi-person-heart me-1"></i>{{ d.donor_user.username }}
|
||||
</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% else %}
|
||||
<span class="text-muted small italic">No volunteers yet</span>
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
</div>
|
||||
</div>
|
||||
<span class="badge {% if br.status == 'Active' %}bg-danger{% elif br.status == 'Accepted' %}bg-info{% else %}bg-secondary{% endif %}">
|
||||
{{ br.status }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="text-center p-4 bg-light rounded border border-dashed">
|
||||
<p class="text-muted small mb-0">You haven't made any blood requests yet.</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="mt-5 border-top pt-4">
|
||||
<h5 class="text-danger fw-bold mb-3">
|
||||
<i class="bi bi-exclamation-triangle-fill me-2"></i>Danger Zone
|
||||
|
||||
@ -55,6 +55,47 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-4">
|
||||
<h5 class="fw-bold mb-3">Impact & Reliability</h5>
|
||||
<div class="row g-3 text-center">
|
||||
<div class="col-6">
|
||||
<div class="p-3 border rounded-3 bg-light">
|
||||
<div class="h2 fw-bold text-danger mb-0">{{ donations_made.count }}</div>
|
||||
<div class="small text-muted text-uppercase fw-bold">Volunteered</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<div class="p-3 border rounded-3 bg-light">
|
||||
<div class="h2 fw-bold text-success mb-0">{{ completed_donations_count }}</div>
|
||||
<div class="small text-muted text-uppercase fw-bold">Successful</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if donations_made %}
|
||||
<div class="mb-4">
|
||||
<h5 class="fw-bold mb-3">Recent Donations</h5>
|
||||
<div class="list-group list-group-flush border rounded-3 overflow-hidden">
|
||||
{% for donation in donations_made|slice:":5" %}
|
||||
<div class="list-group-item bg-light-subtle">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<div>
|
||||
<div class="fw-bold">Donated to {{ donation.request.patient_name }}</div>
|
||||
<div class="small text-muted">{{ donation.request.hospital }} | {{ donation.date|date:"M d, Y" }}</div>
|
||||
</div>
|
||||
{% if donation.is_completed %}
|
||||
<span class="badge bg-success-subtle text-success border border-success-subtle">Verified</span>
|
||||
{% else %}
|
||||
<span class="badge bg-warning-subtle text-warning-emphasis border border-warning-subtle">Pending</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="d-grid gap-2">
|
||||
{% if user.is_authenticated and user != profile_user %}
|
||||
<a href="{% url 'chat' profile_user.username %}" class="btn btn-danger py-2">
|
||||
|
||||
@ -140,9 +140,16 @@ def profile(request):
|
||||
u_form = UserUpdateForm(instance=request.user)
|
||||
p_form = ProfileUpdateForm(instance=profile)
|
||||
|
||||
# Fetch History
|
||||
requests_made = BloodRequest.objects.filter(user=request.user).order_by('-created_at')
|
||||
donations_made = DonationEvent.objects.filter(donor_user=request.user).select_related('request').order_by('-date')
|
||||
|
||||
context = {
|
||||
'u_form': u_form,
|
||||
'p_form': p_form
|
||||
'p_form': p_form,
|
||||
'requests_made': requests_made,
|
||||
'donations_made': donations_made,
|
||||
'completed_donations_count': donations_made.filter(is_completed=True).count()
|
||||
}
|
||||
|
||||
return render(request, 'core/profile.html', context)
|
||||
@ -263,7 +270,12 @@ def home(request):
|
||||
else:
|
||||
donor_list_data.sort(key=lambda x: (-x.is_available, x.name))
|
||||
|
||||
blood_requests = BloodRequest.objects.filter(status='Active').order_by('-urgency', '-created_at')
|
||||
three_days_ago = timezone.now() - timezone.timedelta(days=3)
|
||||
blood_requests = BloodRequest.objects.filter(
|
||||
Q(status='Active') |
|
||||
Q(status='Accepted', accepted_at__gte=three_days_ago)
|
||||
).order_by('-urgency', '-created_at')
|
||||
|
||||
blood_banks = BloodBank.objects.all()
|
||||
|
||||
# Stats for Dashboard
|
||||
@ -275,7 +287,9 @@ def home(request):
|
||||
|
||||
stats = {
|
||||
"total_donors": Donor.objects.count() + demo_donors,
|
||||
"active_requests": BloodRequest.objects.filter(status='Active').count(),
|
||||
"active_requests": BloodRequest.objects.filter(
|
||||
Q(status='Active') | Q(status='Accepted', accepted_at__gte=three_days_ago)
|
||||
).count(),
|
||||
"total_stock": sum([
|
||||
bb.stock_a_plus + bb.stock_a_minus + bb.stock_b_plus + bb.stock_b_minus +
|
||||
bb.stock_o_plus + bb.stock_o_minus + bb.stock_ab_plus + bb.stock_ab_minus
|
||||
@ -412,7 +426,11 @@ def vaccination_info(request):
|
||||
|
||||
def live_map(request):
|
||||
"""View to display live alerts/requests on a map."""
|
||||
active_requests = BloodRequest.objects.filter(status='Active').order_by('-created_at')
|
||||
three_days_ago = timezone.now() - timezone.timedelta(days=3)
|
||||
active_requests = BloodRequest.objects.filter(
|
||||
Q(status='Active') |
|
||||
Q(status='Accepted', accepted_at__gte=three_days_ago)
|
||||
).order_by('-created_at')
|
||||
|
||||
# Also include blood banks and donors optionally if we want a full map
|
||||
# But focusing on alerts as requested.
|
||||
@ -524,6 +542,11 @@ def volunteer_for_request(request, request_id):
|
||||
request=blood_request,
|
||||
donor_user=request.user
|
||||
)
|
||||
# Update request status and timestamp
|
||||
blood_request.status = 'Accepted'
|
||||
blood_request.accepted_at = timezone.now()
|
||||
blood_request.save()
|
||||
|
||||
messages.success(request, "Thank you for volunteering! The requester has been notified.")
|
||||
|
||||
# Notify the requester
|
||||
@ -618,10 +641,16 @@ def public_profile(request, username):
|
||||
profile = user.profile
|
||||
donor_profile = getattr(user, 'donor_profile', None)
|
||||
|
||||
# Fetch History
|
||||
donations_made = DonationEvent.objects.filter(donor_user=user).select_related('request').order_by('-date')
|
||||
completed_donations_count = donations_made.filter(is_completed=True).count()
|
||||
|
||||
context = {
|
||||
'profile_user': user,
|
||||
'profile': profile,
|
||||
'donor': donor_profile,
|
||||
'donations_made': donations_made,
|
||||
'completed_donations_count': completed_donations_count
|
||||
}
|
||||
return render(request, 'core/public_profile.html', context)
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user