diff --git a/core/__pycache__/forms.cpython-311.pyc b/core/__pycache__/forms.cpython-311.pyc new file mode 100644 index 0000000..684052f Binary files /dev/null and b/core/__pycache__/forms.cpython-311.pyc differ diff --git a/core/__pycache__/models.cpython-311.pyc b/core/__pycache__/models.cpython-311.pyc index a179ffa..61b392a 100644 Binary files a/core/__pycache__/models.cpython-311.pyc and b/core/__pycache__/models.cpython-311.pyc differ diff --git a/core/__pycache__/urls.cpython-311.pyc b/core/__pycache__/urls.cpython-311.pyc index 1c540d8..cc49284 100644 Binary files a/core/__pycache__/urls.cpython-311.pyc and b/core/__pycache__/urls.cpython-311.pyc differ diff --git a/core/__pycache__/views.cpython-311.pyc b/core/__pycache__/views.cpython-311.pyc index 976cbe8..a39fa71 100644 Binary files a/core/__pycache__/views.cpython-311.pyc and b/core/__pycache__/views.cpython-311.pyc differ diff --git a/core/forms.py b/core/forms.py new file mode 100644 index 0000000..114e4ff --- /dev/null +++ b/core/forms.py @@ -0,0 +1,27 @@ +from django import forms +from django.contrib.auth.models import User +from .models import UserProfile, BLOOD_GROUPS + +class UserUpdateForm(forms.ModelForm): + email = forms.EmailField() + + class Meta: + model = User + fields = ['username', 'email', 'first_name', 'last_name'] + widgets = { + 'username': forms.TextInput(attrs={'class': 'form-control'}), + 'email': forms.EmailInput(attrs={'class': 'form-control'}), + 'first_name': forms.TextInput(attrs={'class': 'form-control'}), + 'last_name': forms.TextInput(attrs={'class': 'form-control'}), + } + +class ProfileUpdateForm(forms.ModelForm): + class Meta: + model = UserProfile + fields = ['bio', 'location', 'phone', 'blood_group'] + widgets = { + 'bio': forms.Textarea(attrs={'class': 'form-control', 'rows': 3}), + 'location': forms.TextInput(attrs={'class': 'form-control'}), + 'phone': forms.TextInput(attrs={'class': 'form-control'}), + 'blood_group': forms.Select(attrs={'class': 'form-control'}, choices=BLOOD_GROUPS), + } diff --git a/core/migrations/0007_bloodbank_total_capacity.py b/core/migrations/0007_bloodbank_total_capacity.py new file mode 100644 index 0000000..e08250a --- /dev/null +++ b/core/migrations/0007_bloodbank_total_capacity.py @@ -0,0 +1,18 @@ +# Generated by Django 5.2.7 on 2026-02-17 16:06 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0006_bloodrequest_latitude_bloodrequest_longitude'), + ] + + operations = [ + migrations.AddField( + model_name='bloodbank', + name='total_capacity', + field=models.IntegerField(default=1000), + ), + ] diff --git a/core/migrations/0008_userprofile.py b/core/migrations/0008_userprofile.py new file mode 100644 index 0000000..b0208e9 --- /dev/null +++ b/core/migrations/0008_userprofile.py @@ -0,0 +1,28 @@ +# Generated by Django 5.2.7 on 2026-02-17 16:13 + +import django.db.models.deletion +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0007_bloodbank_total_capacity'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name='UserProfile', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('bio', models.TextField(blank=True, max_length=500)), + ('location', models.CharField(blank=True, max_length=100)), + ('birth_date', models.DateField(blank=True, null=True)), + ('phone', models.CharField(blank=True, max_length=20)), + ('blood_group', models.CharField(blank=True, choices=[('A+', 'A+'), ('A-', 'A-'), ('B+', 'B+'), ('B-', 'B-'), ('O+', 'O+'), ('O-', 'O-'), ('AB+', 'AB+'), ('AB-', 'AB-')], max_length=5)), + ('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='profile', to=settings.AUTH_USER_MODEL)), + ], + ), + ] diff --git a/core/migrations/__pycache__/0007_bloodbank_total_capacity.cpython-311.pyc b/core/migrations/__pycache__/0007_bloodbank_total_capacity.cpython-311.pyc new file mode 100644 index 0000000..120d712 Binary files /dev/null and b/core/migrations/__pycache__/0007_bloodbank_total_capacity.cpython-311.pyc differ diff --git a/core/migrations/__pycache__/0008_userprofile.cpython-311.pyc b/core/migrations/__pycache__/0008_userprofile.cpython-311.pyc new file mode 100644 index 0000000..6c0e3a7 Binary files /dev/null and b/core/migrations/__pycache__/0008_userprofile.cpython-311.pyc differ diff --git a/core/models.py b/core/models.py index 1c10aac..367259c 100644 --- a/core/models.py +++ b/core/models.py @@ -1,6 +1,36 @@ from django.db import models from django.utils import timezone from django.contrib.auth.models import User +from django.db.models.signals import post_save +from django.dispatch import receiver + +BLOOD_GROUPS = [ + ('A+', 'A+'), ('A-', 'A-'), + ('B+', 'B+'), ('B-', 'B-'), + ('O+', 'O+'), ('O-', 'O-'), + ('AB+', 'AB+'), ('AB-', 'AB-'), +] + +class UserProfile(models.Model): + user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='profile') + bio = models.TextField(max_length=500, blank=True) + location = models.CharField(max_length=100, blank=True) + birth_date = models.DateField(null=True, blank=True) + phone = models.CharField(max_length=20, blank=True) + blood_group = models.CharField(max_length=5, choices=BLOOD_GROUPS, blank=True) + + def __str__(self): + return self.user.username + +@receiver(post_save, sender=User) +def create_or_save_user_profile(sender, instance, created, **kwargs): + if created: + UserProfile.objects.get_or_create(user=instance) + else: + # For existing users, ensure profile exists + if not hasattr(instance, 'profile'): + UserProfile.objects.create(user=instance) + instance.profile.save() class VaccineRecord(models.Model): user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='vaccine_records') @@ -16,12 +46,6 @@ class VaccineRecord(models.Model): return f"{self.vaccine_name} - Dose {self.dose_number} for {self.user.username}" class Donor(models.Model): - BLOOD_GROUPS = [ - ('A+', 'A+'), ('A-', 'A-'), - ('B+', 'B+'), ('B-', 'B-'), - ('O+', 'O+'), ('O-', 'O-'), - ('AB+', 'AB+'), ('AB-', 'AB-'), - ] name = models.CharField(max_length=100) blood_group = models.CharField(max_length=5, choices=BLOOD_GROUPS) district = models.CharField(max_length=100, default='Kathmandu') @@ -48,7 +72,7 @@ class BloodRequest(models.Model): ('NORMAL', 'Normal'), ] patient_name = models.CharField(max_length=100) - blood_group = models.CharField(max_length=5, choices=Donor.BLOOD_GROUPS) + blood_group = models.CharField(max_length=5, choices=BLOOD_GROUPS) location = models.CharField(max_length=255) urgency = models.CharField(max_length=10, choices=URGENCY_LEVELS, default='NORMAL') hospital = models.CharField(max_length=255) @@ -77,6 +101,7 @@ class BloodBank(models.Model): stock_o_minus = models.IntegerField(default=0) stock_ab_plus = models.IntegerField(default=0) stock_ab_minus = models.IntegerField(default=0) + total_capacity = models.IntegerField(default=1000) def __str__(self): return self.name diff --git a/core/templates/base.html b/core/templates/base.html index 1437535..660a66f 100644 --- a/core/templates/base.html +++ b/core/templates/base.html @@ -202,6 +202,7 @@
  • Live Alerts
  • Vaccination
  • {% if user.is_authenticated %} +
  • My Profile
  • My Records
  • {% endif %}
  • Settings
  • @@ -218,35 +219,40 @@
    -
    +
    - -
    -
    - +
    +
    + Detect Location
    - - {{ current_time|date:"D, M d, Y" }} + + {{ current_time|date:"M d" }}
    {% if user.is_authenticated %}
    - {{ user.username }} - Logout + +
    + +
    + {{ user.username }} +
    + Logout
    {% else %} {% endif %}
    diff --git a/core/templates/core/blood_bank_list.html b/core/templates/core/blood_bank_list.html index d133653..646ce28 100644 --- a/core/templates/core/blood_bank_list.html +++ b/core/templates/core/blood_bank_list.html @@ -48,54 +48,78 @@

    -
    Inventory Levels (Units)
    +
    Inventory Levels (Max {{ bank.total_capacity }} units/type)
    A+
    {{ bank.stock_a_plus }}
    +
    +
    +
    A-
    {{ bank.stock_a_minus }}
    +
    +
    +
    B+
    {{ bank.stock_b_plus }}
    +
    +
    +
    B-
    {{ bank.stock_b_minus }}
    +
    +
    +
    O+
    {{ bank.stock_o_plus }}
    +
    +
    +
    O-
    {{ bank.stock_o_minus }}
    +
    +
    +
    AB+
    {{ bank.stock_ab_plus }}
    +
    +
    +
    AB-
    {{ bank.stock_ab_minus }}
    +
    +
    +
    diff --git a/core/templates/core/index.html b/core/templates/core/index.html index 1e0e15d..e0da796 100644 --- a/core/templates/core/index.html +++ b/core/templates/core/index.html @@ -133,8 +133,8 @@
    -
    {{ stats.total_stock }} Units
    -
    Bank Inventory
    +
    {{ stats.total_stock }} / {{ stats.total_capacity }}
    +
    Inventory vs Capacity
    diff --git a/core/templates/core/profile.html b/core/templates/core/profile.html new file mode 100644 index 0000000..4ecf840 --- /dev/null +++ b/core/templates/core/profile.html @@ -0,0 +1,74 @@ +{% extends 'base.html' %} +{% load static %} + +{% block title %}User Profile - RaktaPulse{% endblock %} + +{% block content %} +
    +
    +
    +
    +
    +
    + +
    +
    +

    {{ user.username }}

    +

    Member since {{ user.date_joined|date:"M d, Y" }}

    +
    +
    + +
    + {% csrf_token %} +
    Account Information
    +
    +
    + + {{ u_form.username }} +
    +
    + + {{ u_form.email }} +
    +
    + + {{ u_form.first_name }} +
    +
    + + {{ u_form.last_name }} +
    +
    + +
    Profile Details
    +
    +
    + + {{ p_form.bio }} +
    Write a short bio about yourself.
    +
    +
    + + {{ p_form.blood_group }} +
    +
    + + {{ p_form.phone }} +
    +
    + + {{ p_form.location }} +
    +
    + +
    + +
    +
    +
    +
    +
    +
    +{% endblock %} diff --git a/core/urls.py b/core/urls.py index f117f9c..5a87ab4 100644 --- a/core/urls.py +++ b/core/urls.py @@ -1,12 +1,13 @@ 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, vaccination_dashboard, add_vaccination, live_map +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, request_blood, profile urlpatterns = [ path("", home, name="home"), path("login/", login_view, name="login"), path("logout/", logout_view, name="logout"), path("register/", register_view, name="register"), + path("profile/", profile, name="profile"), path("donors/", donor_list, name="donor_list"), path("requests/", blood_request_list, name="blood_request_list"), path("banks/", blood_bank_list, name="blood_bank_list"), diff --git a/core/views.py b/core/views.py index 0711e80..ea196c1 100644 --- a/core/views.py +++ b/core/views.py @@ -6,9 +6,34 @@ from django.contrib.auth.forms import UserCreationForm, AuthenticationForm from django.contrib.auth.decorators import login_required from django.contrib import messages from django.utils import timezone -from .models import Donor, BloodRequest, BloodBank, VaccineRecord +from .models import Donor, BloodRequest, BloodBank, VaccineRecord, UserProfile, BLOOD_GROUPS +from .forms import UserUpdateForm, ProfileUpdateForm import math +@login_required +def profile(request): + # Ensure user has a profile + profile, created = UserProfile.objects.get_or_create(user=request.user) + + if request.method == 'POST': + u_form = UserUpdateForm(request.POST, instance=request.user) + p_form = ProfileUpdateForm(request.POST, instance=profile) + if u_form.is_valid() and p_form.is_valid(): + u_form.save() + p_form.save() + messages.success(request, f'Your account has been updated!') + return redirect('profile') + else: + u_form = UserUpdateForm(instance=request.user) + p_form = ProfileUpdateForm(instance=profile) + + context = { + 'u_form': u_form, + 'p_form': p_form + } + + return render(request, 'core/profile.html', context) + def haversine(lat1, lon1, lat2, lon2): # Radius of the Earth in km R = 6371.0 @@ -92,6 +117,7 @@ def home(request): bb.stock_o_plus + bb.stock_o_minus + bb.stock_ab_plus + bb.stock_ab_minus for bb in blood_banks ]), + "total_capacity": sum([bb.total_capacity * 8 for bb in blood_banks]), # 8 blood types "vaccinated_percentage": 0 } @@ -104,7 +130,7 @@ def home(request): "donors": donor_list_data[:8], "blood_requests": blood_requests[:6], "blood_banks": blood_banks, - "blood_groups": [g[0] for g in Donor.BLOOD_GROUPS], + "blood_groups": [g[0] for g in BLOOD_GROUPS], "stats": stats, "project_name": "RaktaPulse", "current_time": timezone.now(), @@ -142,7 +168,7 @@ def donor_list(request): context = { 'donors': donor_list_data, - 'blood_groups': [g[0] for g in Donor.BLOOD_GROUPS], + 'blood_groups': [g[0] for g in BLOOD_GROUPS], } return render(request, 'core/donor_list.html', context) @@ -231,7 +257,7 @@ def request_blood(request): messages.error(request, "Please fill in all required fields.") context = { - 'blood_groups': [g[0] for g in Donor.BLOOD_GROUPS], + 'blood_groups': [g[0] for g in BLOOD_GROUPS], 'urgency_levels': BloodRequest.URGENCY_LEVELS, } return render(request, 'core/request_blood.html', context) diff --git a/reduce_capacity.py b/reduce_capacity.py new file mode 100644 index 0000000..1dbdde6 --- /dev/null +++ b/reduce_capacity.py @@ -0,0 +1,17 @@ +import os +import django + +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'config.settings') +django.setup() + +from core.models import BloodBank + +def run(): + banks = BloodBank.objects.all() + for bank in banks: + bank.total_capacity = 200 # Reduced from 1000 + bank.save() + print("Capacity reduced for all blood banks.") + +if __name__ == "__main__": + run()