diff --git a/config/__pycache__/settings.cpython-311.pyc b/config/__pycache__/settings.cpython-311.pyc index 50ad71d..404de93 100644 Binary files a/config/__pycache__/settings.cpython-311.pyc and b/config/__pycache__/settings.cpython-311.pyc differ diff --git a/config/__pycache__/urls.cpython-311.pyc b/config/__pycache__/urls.cpython-311.pyc index 8cde51e..3c20f11 100644 Binary files a/config/__pycache__/urls.cpython-311.pyc and b/config/__pycache__/urls.cpython-311.pyc differ diff --git a/config/settings.py b/config/settings.py index 103f1eb..cd49a45 100644 --- a/config/settings.py +++ b/config/settings.py @@ -193,3 +193,6 @@ DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' LOGIN_URL = '/login/' LOGIN_REDIRECT_URL = '/' + +MEDIA_URL = '/media/' +MEDIA_ROOT = BASE_DIR / 'media' diff --git a/config/urls.py b/config/urls.py index baccfac..701370b 100644 --- a/config/urls.py +++ b/config/urls.py @@ -28,3 +28,4 @@ urlpatterns = [ if settings.DEBUG: urlpatterns += static("/assets/", document_root=settings.BASE_DIR / "assets") urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT) + urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) diff --git a/core/__pycache__/forms.cpython-311.pyc b/core/__pycache__/forms.cpython-311.pyc index 684052f..6c37854 100644 Binary files a/core/__pycache__/forms.cpython-311.pyc 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 ffaadc6..3d8eb68 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 77f725c..116ac38 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 c9d8df3..2dedc28 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 index 114e4ff..7f0ecea 100644 --- a/core/forms.py +++ b/core/forms.py @@ -2,6 +2,18 @@ from django import forms from django.contrib.auth.models import User from .models import UserProfile, BLOOD_GROUPS +from django.contrib.auth.forms import UserCreationForm + +class UserRegisterForm(UserCreationForm): + email = forms.EmailField(required=True) + blood_group = forms.ChoiceField(choices=BLOOD_GROUPS, required=True) + location = forms.CharField(max_length=255, required=True) + phone = forms.CharField(max_length=20, required=True) + + class Meta: + model = User + fields = ['username', 'email'] + class UserUpdateForm(forms.ModelForm): email = forms.EmailField() @@ -18,10 +30,11 @@ class UserUpdateForm(forms.ModelForm): class ProfileUpdateForm(forms.ModelForm): class Meta: model = UserProfile - fields = ['bio', 'location', 'phone', 'blood_group'] + fields = ['bio', 'location', 'phone', 'blood_group', 'profile_pic'] 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), + 'profile_pic': forms.FileInput(attrs={'class': 'form-control'}), } diff --git a/core/management/__init__.py b/core/management/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/core/management/__pycache__/__init__.cpython-311.pyc b/core/management/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000..80f2921 Binary files /dev/null and b/core/management/__pycache__/__init__.cpython-311.pyc differ diff --git a/core/management/commands/__init__.py b/core/management/commands/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/core/management/commands/__pycache__/__init__.cpython-311.pyc b/core/management/commands/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000..822ff85 Binary files /dev/null and b/core/management/commands/__pycache__/__init__.cpython-311.pyc differ diff --git a/core/management/commands/__pycache__/cleanup_requests.cpython-311.pyc b/core/management/commands/__pycache__/cleanup_requests.cpython-311.pyc new file mode 100644 index 0000000..f75a249 Binary files /dev/null and b/core/management/commands/__pycache__/cleanup_requests.cpython-311.pyc differ diff --git a/core/management/commands/__pycache__/seed_hospitals.cpython-311.pyc b/core/management/commands/__pycache__/seed_hospitals.cpython-311.pyc new file mode 100644 index 0000000..b243ce4 Binary files /dev/null and b/core/management/commands/__pycache__/seed_hospitals.cpython-311.pyc differ diff --git a/core/management/commands/cleanup_requests.py b/core/management/commands/cleanup_requests.py new file mode 100644 index 0000000..b307ced --- /dev/null +++ b/core/management/commands/cleanup_requests.py @@ -0,0 +1,46 @@ +from django.core.management.base import BaseCommand +from django.utils import timezone +from core.models import BloodRequest +from datetime import timedelta + +class Command(BaseCommand): + help = 'Deletes old blood requests based on urgency and status' + + def handle(self, *args, **options): + now = timezone.now() + + # Define thresholds (days) + thresholds = { + 'CRITICAL': 3, + 'URGENT': 7, + 'NORMAL': 15, + } + + deleted_count = 0 + + # 1. Delete by Urgency + for urgency, days in thresholds.items(): + cutoff = now - timedelta(days=days) + old_requests = BloodRequest.objects.filter( + urgency=urgency, + created_at__lt=cutoff + ) + count = old_requests.count() + if count > 0: + old_requests.delete() + 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 + cutoff_inactive = now - timedelta(days=7) + inactive_requests = BloodRequest.objects.exclude(status='Active').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.')) + deleted_count += count_inactive + + if deleted_count == 0: + self.stdout.write(self.style.SUCCESS('No old requests to delete.')) + else: + self.stdout.write(self.style.SUCCESS(f'Cleanup complete. Total deleted: {deleted_count}')) diff --git a/core/management/commands/seed_hospitals.py b/core/management/commands/seed_hospitals.py new file mode 100644 index 0000000..5600d30 --- /dev/null +++ b/core/management/commands/seed_hospitals.py @@ -0,0 +1,47 @@ +from django.core.management.base import BaseCommand +from core.models import Hospital + +class Command(BaseCommand): + help = 'Seed the database with Kathmandu hospitals and coordinates' + + def handle(self, *args, **kwargs): + hospitals = [ + {"name": "Bir Hospital", "location": "Kanti Path, Kathmandu", "phone": "01-4221988", "lat": 27.7058, "lng": 85.3135}, + {"name": "Kathmandu Valley Hospital", "location": "Bagh darbar marg, Kathmandu", "phone": "01-4255330", "lat": 27.7015, "lng": 85.3115}, + {"name": "Civil Service Hospital of Nepal", "location": "Minbhawan marg, Kathmandu", "phone": "01-4793000", "website": "https://csh.gov.np/ne", "lat": 27.6841, "lng": 85.3385}, + {"name": "Venus Hospital", "location": "Devkota Sadak, Kathmandu", "phone": "01-4475120", "lat": 27.6945, "lng": 85.3415}, + {"name": "Vayodha Hospital", "location": "Balkhu, Kathmandu", "phone": "01-4281666", "website": "https://www.vayodhahospitals.com/", "lat": 27.6855, "lng": 85.2935}, + {"name": "Grande City Hospital", "location": "Kanti path, Kathmandu", "phone": "01-4163500", "website": "http://grandecityhospital.com/", "lat": 27.7065, "lng": 85.3135}, + {"name": "Teaching Hospital", "location": "Maharajgunj, Kathmandu", "phone": "01-4412303", "website": "http://iom.edu.np/", "lat": 27.7351, "lng": 85.3315}, + {"name": "Kathmandu Hospital", "location": "Tripureshwor marg, Kathmandu", "phone": "01-4229656", "lat": 27.6935, "lng": 85.3145}, + {"name": "Kathmandu Neuro & General Hospital", "location": "Bagh Durbar marg, Kathmandu", "phone": "01-5327735", "lat": 27.7018, "lng": 85.3118}, + {"name": "Teku Hospital", "location": "Teku, Kathmandu", "phone": "01-4253396", "website": "http://www.stidh.gov.np/", "lat": 27.6961, "lng": 85.3025}, + {"name": "Nepal Eye Hospital", "location": "Tripureswor, Kathmandu", "phone": "01-4260813", "website": "https://www.nepaleyehospital.org/", "lat": 27.6940, "lng": 85.3140}, + {"name": "Everest Hospital Pvt. Ltd.", "location": "Kathmandu", "phone": "01-4793024", "website": "http://everesthospital.org.np/", "lat": 27.6925, "lng": 85.3345}, + {"name": "Om Hospital & Research Center", "location": "Chabil, Kathmandu", "phone": "01-4476225", "website": "https://omhospitalnepal.com/", "lat": 27.7175, "lng": 85.3485}, + {"name": "Annapurna Neuro Hospital", "location": "Maitighar mandala", "phone": "01-4256656", "website": "https://www.annapurnahospitals.com", "lat": 27.6945, "lng": 85.3215}, + {"name": "Norvic International Hospital", "location": "Kathmandu", "phone": "01-5970032", "website": "https://www.norvichospital.com/", "lat": 27.6897, "lng": 85.3235}, + {"name": "Blue Cross Hospital", "location": "Tripura marga, Kathmandu", "phone": "01-4262027", "lat": 27.6948, "lng": 85.3148}, + {"name": "Paropakar Maternity and Womens’ Hospital", "location": "Thapathali, Kathmandu", "phone": "01-4261363", "lat": 27.6915, "lng": 85.3215}, + {"name": "ERA INTERNATIONAL HOSPITAL Pvt. Ltd.", "location": "Kathmandu", "phone": "01-4352447", "website": "https://www.era-hospital.com/", "lat": 27.7215, "lng": 85.3015}, + {"name": "Birendra Military Hospital", "location": "Kathmandu", "phone": "01-4271941", "website": "https://birendrahospital.nepalarmy.mil.np/", "lat": 27.7085, "lng": 85.2815}, + {"name": "Capital Hospital", "location": "Kathmandu", "phone": "01-4168222", "lat": 27.7045, "lng": 85.3215}, + {"name": "Valley Maternity Nursing Home", "location": "Kathmandu", "phone": "01-4420224", "lat": 27.7125, "lng": 85.3245}, + {"name": "Medicare National Hospital & Research Center", "location": "Ring road, kathmandu", "phone": "01-4467067", "website": "http://medicarehosp.com/", "lat": 27.7145, "lng": 85.3415}, + {"name": "CIWEC Hospital Pvt. Ltd.", "location": "Kathmandu", "phone": "01-4435232", "lat": 27.7165, "lng": 85.3215}, + {"name": "Nepal-Bharat Maitri Hospital", "location": "Dakshinha murti marga, Kathmandu", "phone": "01-5241288", "lat": 27.7185, "lng": 85.3515}, + {"name": "Kantipur Hospital", "location": "Shriganesh Marg, Tripureshwor", "phone": "01-4111", "lat": 27.6938, "lng": 85.3142}, + ] + + for h_data in hospitals: + Hospital.objects.update_or_create( + name=h_data['name'], + defaults={ + 'location': h_data['location'], + 'phone': h_data['phone'], + 'website': h_data.get('website', ''), + 'latitude': h_data.get('lat'), + 'longitude': h_data.get('lng'), + } + ) + self.stdout.write(self.style.SUCCESS(f'Successfully updated {len(hospitals)} hospitals with coordinates.')) diff --git a/core/migrations/0010_delete_feedback.py b/core/migrations/0010_delete_feedback.py new file mode 100644 index 0000000..3756f65 --- /dev/null +++ b/core/migrations/0010_delete_feedback.py @@ -0,0 +1,16 @@ +# Generated by Django 5.2.7 on 2026-02-18 05:57 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0009_bloodrequest_user_donor_user_donationevent_feedback_and_more'), + ] + + operations = [ + migrations.DeleteModel( + name='Feedback', + ), + ] diff --git a/core/migrations/0011_hospital.py b/core/migrations/0011_hospital.py new file mode 100644 index 0000000..be6635e --- /dev/null +++ b/core/migrations/0011_hospital.py @@ -0,0 +1,23 @@ +# Generated by Django 5.2.7 on 2026-02-18 06:05 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0010_delete_feedback'), + ] + + operations = [ + migrations.CreateModel( + name='Hospital', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=255)), + ('location', models.CharField(max_length=255)), + ('phone', models.CharField(blank=True, max_length=100, null=True)), + ('website', models.URLField(blank=True, null=True)), + ], + ), + ] diff --git a/core/migrations/0012_hospital_latitude_hospital_longitude.py b/core/migrations/0012_hospital_latitude_hospital_longitude.py new file mode 100644 index 0000000..ba98870 --- /dev/null +++ b/core/migrations/0012_hospital_latitude_hospital_longitude.py @@ -0,0 +1,23 @@ +# Generated by Django 5.2.7 on 2026-02-18 06:19 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0011_hospital'), + ] + + operations = [ + migrations.AddField( + model_name='hospital', + name='latitude', + field=models.DecimalField(blank=True, decimal_places=6, max_digits=9, null=True), + ), + migrations.AddField( + model_name='hospital', + name='longitude', + field=models.DecimalField(blank=True, decimal_places=6, max_digits=9, null=True), + ), + ] diff --git a/core/migrations/0013_userprofile_profile_pic.py b/core/migrations/0013_userprofile_profile_pic.py new file mode 100644 index 0000000..095f467 --- /dev/null +++ b/core/migrations/0013_userprofile_profile_pic.py @@ -0,0 +1,18 @@ +# Generated by Django 5.2.7 on 2026-02-18 07:10 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0012_hospital_latitude_hospital_longitude'), + ] + + operations = [ + migrations.AddField( + model_name='userprofile', + name='profile_pic', + field=models.ImageField(blank=True, null=True, upload_to='profile_pics'), + ), + ] diff --git a/core/migrations/__pycache__/0010_delete_feedback.cpython-311.pyc b/core/migrations/__pycache__/0010_delete_feedback.cpython-311.pyc new file mode 100644 index 0000000..adbdc32 Binary files /dev/null and b/core/migrations/__pycache__/0010_delete_feedback.cpython-311.pyc differ diff --git a/core/migrations/__pycache__/0011_hospital.cpython-311.pyc b/core/migrations/__pycache__/0011_hospital.cpython-311.pyc new file mode 100644 index 0000000..bab95c1 Binary files /dev/null and b/core/migrations/__pycache__/0011_hospital.cpython-311.pyc differ diff --git a/core/migrations/__pycache__/0012_hospital_latitude_hospital_longitude.cpython-311.pyc b/core/migrations/__pycache__/0012_hospital_latitude_hospital_longitude.cpython-311.pyc new file mode 100644 index 0000000..6039d83 Binary files /dev/null and b/core/migrations/__pycache__/0012_hospital_latitude_hospital_longitude.cpython-311.pyc differ diff --git a/core/migrations/__pycache__/0013_userprofile_profile_pic.cpython-311.pyc b/core/migrations/__pycache__/0013_userprofile_profile_pic.cpython-311.pyc new file mode 100644 index 0000000..6041900 Binary files /dev/null and b/core/migrations/__pycache__/0013_userprofile_profile_pic.cpython-311.pyc differ diff --git a/core/models.py b/core/models.py index 00c3cec..60feb33 100644 --- a/core/models.py +++ b/core/models.py @@ -18,6 +18,7 @@ class UserProfile(models.Model): 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) + profile_pic = models.ImageField(upload_to='profile_pics', blank=True, null=True) def __str__(self): return self.user.username @@ -32,6 +33,16 @@ def create_or_save_user_profile(sender, instance, created, **kwargs): UserProfile.objects.create(user=instance) instance.profile.save() +@receiver(post_save, sender=UserProfile) +def sync_donor_profile(sender, instance, **kwargs): + if instance.blood_group: + donor, created = Donor.objects.get_or_create(user=instance.user) + donor.name = f"{instance.user.first_name} {instance.user.last_name}".strip() or instance.user.username + donor.blood_group = instance.blood_group + donor.location = instance.location + donor.phone = instance.phone + donor.save() + class VaccineRecord(models.Model): user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='vaccine_records') vaccine_name = models.CharField(max_length=100) @@ -66,6 +77,17 @@ class Donor(models.Model): def __str__(self): return f"{self.name} ({self.blood_group}) - {'Verified' if self.is_verified else 'Unverified'}" +class Hospital(models.Model): + name = 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) + phone = models.CharField(max_length=100, null=True, blank=True) + website = models.URLField(null=True, blank=True) + + def __str__(self): + return self.name + class BloodRequest(models.Model): URGENCY_LEVELS = [ ('CRITICAL', 'Critical'), @@ -126,12 +148,3 @@ class Notification(models.Model): def __str__(self): return f"Notification for {self.user.username}: {self.message[:20]}..." - -class Feedback(models.Model): - user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='feedbacks') - content = models.TextField() - rating = models.PositiveIntegerField(default=5) - created_at = models.DateTimeField(auto_now_add=True) - - def __str__(self): - return f"Feedback from {self.user.username} - {self.rating} stars" diff --git a/core/templates/base.html b/core/templates/base.html index 6f5d13f..70f3723 100644 --- a/core/templates/base.html +++ b/core/templates/base.html @@ -218,6 +218,10 @@ z-index: -1; } + .dropdown-toggle.no-caret::after { + display: none; + } + .sos-content { display: flex; flex-direction: column; @@ -272,11 +276,10 @@
{% trans "We value your experience. Please let us know how we can improve." %}
- - -A comprehensive list of public and private hospitals for blood requests and emergency care.
+{{ hospital.location }}
+No hospitals registered in the system.
+Member since {{ user.date_joined|date:"M d, Y" }}