diff --git a/civicfix/civicfix/__pycache__/settings.cpython-313.pyc b/civicfix/civicfix/__pycache__/settings.cpython-313.pyc index 8f43669..0fbcdc9 100644 Binary files a/civicfix/civicfix/__pycache__/settings.cpython-313.pyc and b/civicfix/civicfix/__pycache__/settings.cpython-313.pyc differ diff --git a/civicfix/civicfix/settings.py b/civicfix/civicfix/settings.py index a2b6db2..9212d0c 100644 --- a/civicfix/civicfix/settings.py +++ b/civicfix/civicfix/settings.py @@ -123,4 +123,4 @@ DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' # Add these settings LOGIN_REDIRECT_URL = 'citizen_dashboard' LOGIN_URL = 'login' -LOGOUT_REDIRECT_URL = 'home' +LOGOUT_REDIRECT_URL = 'home' \ No newline at end of file diff --git a/civicfix/core/__pycache__/forms.cpython-313.pyc b/civicfix/core/__pycache__/forms.cpython-313.pyc index 23e47e2..8cfac62 100644 Binary files a/civicfix/core/__pycache__/forms.cpython-313.pyc and b/civicfix/core/__pycache__/forms.cpython-313.pyc differ diff --git a/civicfix/core/__pycache__/models.cpython-313.pyc b/civicfix/core/__pycache__/models.cpython-313.pyc index a1defd5..a6af577 100644 Binary files a/civicfix/core/__pycache__/models.cpython-313.pyc and b/civicfix/core/__pycache__/models.cpython-313.pyc differ diff --git a/civicfix/core/__pycache__/urls.cpython-313.pyc b/civicfix/core/__pycache__/urls.cpython-313.pyc index b8179ac..864507a 100644 Binary files a/civicfix/core/__pycache__/urls.cpython-313.pyc and b/civicfix/core/__pycache__/urls.cpython-313.pyc differ diff --git a/civicfix/core/__pycache__/views.cpython-313.pyc b/civicfix/core/__pycache__/views.cpython-313.pyc index 4a4eff7..bc4512a 100644 Binary files a/civicfix/core/__pycache__/views.cpython-313.pyc and b/civicfix/core/__pycache__/views.cpython-313.pyc differ diff --git a/civicfix/core/forms.py b/civicfix/core/forms.py index 7785e7d..5d92257 100644 --- a/civicfix/core/forms.py +++ b/civicfix/core/forms.py @@ -23,7 +23,7 @@ class CitizenRegistrationForm(UserCreationForm): class IssueForm(forms.ModelForm): class Meta: model = Issue - fields = ['title', 'description', 'category', 'location', 'latitude', 'longitude', 'photo'] + fields = ['title', 'description', 'location', 'latitude', 'longitude', 'photo'] widgets = { 'latitude': forms.HiddenInput(), 'longitude': forms.HiddenInput(), diff --git a/civicfix/core/migrations/0001_initial.py b/civicfix/core/migrations/0001_initial.py index 1478ea0..dfcdd2a 100644 --- a/civicfix/core/migrations/0001_initial.py +++ b/civicfix/core/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 5.2.5 on 2025-08-20 06:30 +# Generated by Django 5.2.5 on 2025-08-26 06:15 import django.contrib.auth.models import django.contrib.auth.validators @@ -18,15 +18,6 @@ class Migration(migrations.Migration): ] operations = [ - migrations.CreateModel( - name='IssueCategory', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(max_length=100)), - ('description', models.TextField(blank=True)), - ('icon', models.CharField(default='fas fa-exclamation-circle', max_length=50)), - ], - ), migrations.CreateModel( name='User', fields=[ @@ -57,21 +48,64 @@ class Migration(migrations.Migration): ('objects', django.contrib.auth.models.UserManager()), ], ), + migrations.CreateModel( + name='Department', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=100, unique=True)), + ('description', models.TextField(blank=True, null=True)), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('admin', models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='admin_of_department', to=settings.AUTH_USER_MODEL)), + ('users', models.ManyToManyField(blank=True, related_name='departments', to=settings.AUTH_USER_MODEL)), + ], + options={ + 'ordering': ['name'], + }, + ), migrations.CreateModel( name='Issue', fields=[ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('title', models.CharField(max_length=200)), ('description', models.TextField()), - ('location', models.CharField(max_length=200)), - ('latitude', models.FloatField()), - ('longitude', models.FloatField()), + ('location', models.CharField(blank=True, max_length=200)), + ('latitude', models.FloatField(blank=True, null=True)), + ('longitude', models.FloatField(blank=True, null=True)), ('photo', models.ImageField(blank=True, null=True, upload_to='issue_photos/', validators=[django.core.validators.FileExtensionValidator(['jpg', 'jpeg', 'png', 'gif'])])), ('status', models.CharField(choices=[('reported', 'Reported'), ('acknowledged', 'Acknowledged'), ('in_progress', 'In Progress'), ('resolved', 'Resolved')], default='reported', max_length=20)), ('created_at', models.DateTimeField(auto_now_add=True)), ('updated_at', models.DateTimeField(auto_now=True)), + ('department', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='issues', to='core.department')), ('reporter', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='reported_issues', to=settings.AUTH_USER_MODEL)), - ('category', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='core.issuecategory')), ], + options={ + 'ordering': ['-created_at'], + }, + ), + migrations.CreateModel( + name='Comment', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('content', models.TextField()), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('parent', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='replies', to='core.comment')), + ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ('issue', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='comments', to='core.issue')), + ], + options={ + 'ordering': ['created_at'], + }, + ), + migrations.CreateModel( + name='Vote', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('issue', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='votes', to='core.issue')), + ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ], + options={ + 'unique_together': {('user', 'issue')}, + }, ), ] diff --git a/civicfix/core/migrations/0002_vote.py b/civicfix/core/migrations/0002_vote.py deleted file mode 100644 index 6667ca9..0000000 --- a/civicfix/core/migrations/0002_vote.py +++ /dev/null @@ -1,27 +0,0 @@ -# Generated by Django 5.2.5 on 2025-08-21 22:31 - -import django.db.models.deletion -from django.conf import settings -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('core', '0001_initial'), - ] - - operations = [ - migrations.CreateModel( - name='Vote', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('created_at', models.DateTimeField(auto_now_add=True)), - ('issue', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='votes', to='core.issue')), - ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), - ], - options={ - 'unique_together': {('user', 'issue')}, - }, - ), - ] diff --git a/civicfix/core/migrations/0003_comment.py b/civicfix/core/migrations/0003_comment.py deleted file mode 100644 index bae5ef4..0000000 --- a/civicfix/core/migrations/0003_comment.py +++ /dev/null @@ -1,29 +0,0 @@ -# Generated by Django 5.2.5 on 2025-08-22 00:55 - -import django.db.models.deletion -from django.conf import settings -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('core', '0002_vote'), - ] - - operations = [ - migrations.CreateModel( - name='Comment', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('content', models.TextField()), - ('created_at', models.DateTimeField(auto_now_add=True)), - ('issue', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='comments', to='core.issue')), - ('parent', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='replies', to='core.comment')), - ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='comments', to=settings.AUTH_USER_MODEL)), - ], - options={ - 'ordering': ['created_at'], - }, - ), - ] diff --git a/civicfix/core/migrations/0004_department_alter_comment_user.py b/civicfix/core/migrations/0004_department_alter_comment_user.py deleted file mode 100644 index 1d8bf39..0000000 --- a/civicfix/core/migrations/0004_department_alter_comment_user.py +++ /dev/null @@ -1,32 +0,0 @@ -# Generated by Django 5.2.5 on 2025-08-25 06:21 - -import django.db.models.deletion -from django.conf import settings -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('core', '0003_comment'), - ] - - operations = [ - migrations.CreateModel( - name='Department', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(max_length=100, unique=True)), - ('description', models.TextField(blank=True, null=True)), - ('created_at', models.DateTimeField(auto_now_add=True)), - ], - options={ - 'ordering': ['name'], - }, - ), - migrations.AlterField( - model_name='comment', - name='user', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL), - ), - ] diff --git a/civicfix/core/migrations/0005_department_users.py b/civicfix/core/migrations/0005_department_users.py deleted file mode 100644 index e0c4aac..0000000 --- a/civicfix/core/migrations/0005_department_users.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 5.2.5 on 2025-08-25 06:43 - -from django.conf import settings -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('core', '0004_department_alter_comment_user'), - ] - - operations = [ - migrations.AddField( - model_name='department', - name='users', - field=models.ManyToManyField(blank=True, related_name='departments', to=settings.AUTH_USER_MODEL), - ), - ] diff --git a/civicfix/core/migrations/0006_remove_department_users_department_admin.py b/civicfix/core/migrations/0006_remove_department_users_department_admin.py deleted file mode 100644 index 90852f5..0000000 --- a/civicfix/core/migrations/0006_remove_department_users_department_admin.py +++ /dev/null @@ -1,24 +0,0 @@ -# Generated by Django 5.2.5 on 2025-08-25 07:20 - -import django.db.models.deletion -from django.conf import settings -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('core', '0005_department_users'), - ] - - operations = [ - migrations.RemoveField( - model_name='department', - name='users', - ), - migrations.AddField( - model_name='department', - name='admin', - field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='admin_of_department', to=settings.AUTH_USER_MODEL), - ), - ] diff --git a/civicfix/core/migrations/0007_department_users.py b/civicfix/core/migrations/0007_department_users.py deleted file mode 100644 index 30a4b8f..0000000 --- a/civicfix/core/migrations/0007_department_users.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 5.2.5 on 2025-08-25 07:24 - -from django.conf import settings -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('core', '0006_remove_department_users_department_admin'), - ] - - operations = [ - migrations.AddField( - model_name='department', - name='users', - field=models.ManyToManyField(blank=True, related_name='departments', to=settings.AUTH_USER_MODEL), - ), - ] diff --git a/civicfix/core/migrations/0008_alter_issue_options_alter_issue_category_and_more.py b/civicfix/core/migrations/0008_alter_issue_options_alter_issue_category_and_more.py deleted file mode 100644 index 3a08f75..0000000 --- a/civicfix/core/migrations/0008_alter_issue_options_alter_issue_category_and_more.py +++ /dev/null @@ -1,38 +0,0 @@ -# Generated by Django 5.2.5 on 2025-08-25 08:23 - -import django.db.models.deletion -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('core', '0007_department_users'), - ] - - operations = [ - migrations.AlterModelOptions( - name='issue', - options={'ordering': ['-created_at']}, - ), - migrations.AlterField( - model_name='issue', - name='category', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='issues', to='core.issuecategory'), - ), - migrations.AlterField( - model_name='issue', - name='latitude', - field=models.FloatField(blank=True, null=True), - ), - migrations.AlterField( - model_name='issue', - name='location', - field=models.CharField(blank=True, max_length=200), - ), - migrations.AlterField( - model_name='issue', - name='longitude', - field=models.FloatField(blank=True, null=True), - ), - ] diff --git a/civicfix/core/migrations/0009_issue_department.py b/civicfix/core/migrations/0009_issue_department.py deleted file mode 100644 index c74cace..0000000 --- a/civicfix/core/migrations/0009_issue_department.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 5.2.5 on 2025-08-25 08:28 - -import django.db.models.deletion -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('core', '0008_alter_issue_options_alter_issue_category_and_more'), - ] - - operations = [ - migrations.AddField( - model_name='issue', - name='department', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='issues', to='core.department'), - ), - ] diff --git a/civicfix/core/migrations/__pycache__/0001_initial.cpython-313.pyc b/civicfix/core/migrations/__pycache__/0001_initial.cpython-313.pyc index 4854e70..ceb0996 100644 Binary files a/civicfix/core/migrations/__pycache__/0001_initial.cpython-313.pyc and b/civicfix/core/migrations/__pycache__/0001_initial.cpython-313.pyc differ diff --git a/civicfix/core/migrations/__pycache__/0010_remove_issue_category_delete_issuecategory.cpython-313.pyc b/civicfix/core/migrations/__pycache__/0010_remove_issue_category_delete_issuecategory.cpython-313.pyc new file mode 100644 index 0000000..b49fb81 Binary files /dev/null and b/civicfix/core/migrations/__pycache__/0010_remove_issue_category_delete_issuecategory.cpython-313.pyc differ diff --git a/civicfix/core/migrations/__pycache__/0011_delete_issuecategory.cpython-313.pyc b/civicfix/core/migrations/__pycache__/0011_delete_issuecategory.cpython-313.pyc new file mode 100644 index 0000000..e6c55b4 Binary files /dev/null and b/civicfix/core/migrations/__pycache__/0011_delete_issuecategory.cpython-313.pyc differ diff --git a/civicfix/core/migrations/__pycache__/0012_issuecategory_issue_category.cpython-313.pyc b/civicfix/core/migrations/__pycache__/0012_issuecategory_issue_category.cpython-313.pyc new file mode 100644 index 0000000..1e58de8 Binary files /dev/null and b/civicfix/core/migrations/__pycache__/0012_issuecategory_issue_category.cpython-313.pyc differ diff --git a/civicfix/core/models.py b/civicfix/core/models.py index 36dcebc..318a41c 100644 --- a/civicfix/core/models.py +++ b/civicfix/core/models.py @@ -53,14 +53,6 @@ class Department(models.Model): def __str__(self): return self.name -class IssueCategory(models.Model): - name = models.CharField(max_length=100) - description = models.TextField(blank=True) - icon = models.CharField(max_length=50, default='fas fa-exclamation-circle') - - def __str__(self): - return self.name - class Issue(models.Model): STATUS_REPORTED = 'reported' STATUS_ACKNOWLEDGED = 'acknowledged' @@ -77,14 +69,6 @@ class Issue(models.Model): title = models.CharField(max_length=200) description = models.TextField() - category = models.ForeignKey( - "IssueCategory", - on_delete=models.SET_NULL, - null=True, - blank=True, - related_name="issues" - ) - reporter = models.ForeignKey( settings.AUTH_USER_MODEL, on_delete=models.CASCADE, diff --git a/civicfix/core/templates/core/base.html b/civicfix/core/templates/core/base.html index 1bea865..d4f6dd7 100644 --- a/civicfix/core/templates/core/base.html +++ b/civicfix/core/templates/core/base.html @@ -89,7 +89,8 @@ {% if user.is_resolver %}
  • - Department Dashboard + + Department Dashboard
  • {% endif %} diff --git a/civicfix/core/templates/core/index.html b/civicfix/core/templates/core/index.html index 1000069..c7324b9 100644 --- a/civicfix/core/templates/core/index.html +++ b/civicfix/core/templates/core/index.html @@ -141,8 +141,18 @@ No image {% endif %}
    - - {{ issue.category.name|default:"General" }} + + {{ issue.department.name|default:"General" }}
    {{ issue.title|truncatewords:5 }}

    {{ issue.description|truncatewords:15 }}

    diff --git a/civicfix/core/templates/dashboard/department_dashboard.html b/civicfix/core/templates/dashboard/department_dashboard.html index 16a50e2..59d8ec5 100644 --- a/civicfix/core/templates/dashboard/department_dashboard.html +++ b/civicfix/core/templates/dashboard/department_dashboard.html @@ -5,8 +5,13 @@

    Department Dashboard

    -

    Welcome, {{ request.user.username }}. Your Departments: - {% for dept in departments %} {{ dept.name }}{% if not forloop.last %}, {% endif %}{% endfor %} +

    + Welcome, {{ request.user.username }}. Your Departments: + {% for dept in departments %} + {{ dept.name }} + {% empty %} + No department assigned. + {% endfor %}

    @@ -20,6 +25,7 @@ Reported By Status Created At + Action @@ -43,6 +49,23 @@ {% endif %} {{ issue.created_at|date:"M d, Y H:i" }} + + {% if issue.status in "reported,acknowledged" %} +
    + {% csrf_token %} + + +
    + {% elif issue.status == "in_progress" %} +
    + {% csrf_token %} + + +
    + {% else %} + No actions + {% endif %} + {% endfor %} diff --git a/civicfix/core/urls.py b/civicfix/core/urls.py index c90588d..6f8d3dd 100644 --- a/civicfix/core/urls.py +++ b/civicfix/core/urls.py @@ -20,4 +20,5 @@ urlpatterns = [ path("issues//comment//", views.add_comment, name="add_comment"), path('vote//', views.vote_issue, name='vote_issue'), path("department/", views.department_dashboard, name="department_dashboard"), + path("update-issue-status//", views.update_issue_status, name="update_issue_status"), ] \ No newline at end of file diff --git a/civicfix/core/views.py b/civicfix/core/views.py index 33fda57..08d085c 100644 --- a/civicfix/core/views.py +++ b/civicfix/core/views.py @@ -8,7 +8,7 @@ from django.db.models import Exists, OuterRef from django.http import JsonResponse from django.shortcuts import render, redirect, get_object_or_404 from django.views.decorators.http import require_POST -from .models import Issue, IssueCategory, User, Vote, Comment, Department +from .models import Issue, User, Vote, Comment, Department from .forms import CitizenRegistrationForm, IssueForm, CommentForm, IssueAssignForm def home(request): @@ -16,15 +16,12 @@ def home(request): resolved_issues = Issue.objects.filter(status='resolved').count() active_users = User.objects.filter(is_active=True).count() - # Get recently reported issues (last 3 issues) recent_issues = Issue.objects.all().order_by('-created_at')[:3] for issue in recent_issues: issue.user_has_voted = issue.has_user_voted(request.user) if request.user.is_authenticated else False - # Get municipal departments count (assuming you have a Department model) - # If you don't have one yet, you can use a placeholder or create the model - municipal_departments = 5 # Placeholder - replace with actual count when you have the model + municipal_departments = 5 context = { 'total_issues': total_issues, @@ -46,12 +43,10 @@ def citizen_dashboard(request): all_user_issues = Issue.objects.filter(reporter=request.user) user_issues_display = all_user_issues.order_by('-created_at')[:5] resolved_count = all_user_issues.filter(status='resolved').count() - categories = IssueCategory.objects.all() context = { 'user_issues': user_issues_display, 'resolved_count': resolved_count, - 'categories': categories, 'issue_form': IssueForm(), } return render(request, 'dashboard/citizen_dashboard.html', context) @@ -67,6 +62,7 @@ def report_issue(request): if form.is_valid(): issue = form.save(commit=False) issue.reporter = request.user + issue.status = "reported" # default status issue.save() messages.success(request, 'Issue reported successfully!') return redirect('citizen_dashboard') @@ -87,26 +83,19 @@ def view_all_issues(request): user_vote_subq = Vote.objects.filter(user=request.user, issue_id=OuterRef('pk')) issues = ( Issue.objects - .select_related('category', 'reporter') + .select_related('reporter') .annotate(user_has_voted=Exists(user_vote_subq)) .order_by('-created_at') ) - categories = IssueCategory.objects.all() - - # Optional filters (status/category) if you wired the dropdowns + # Optional filter (status only now) status = request.GET.get('status') or '' - category_id = request.GET.get('category') or '' if status: issues = issues.filter(status=status) - if category_id: - issues = issues.filter(category_id=category_id) return render(request, 'issues/view_all_issues.html', { 'issues': issues, - 'categories': categories, 'selected_status': status, - 'selected_category': category_id, }) @@ -307,6 +296,24 @@ def resolver_check(user): return user.is_resolver @login_required -@user_passes_test(resolver_check) +@user_passes_test(lambda u: u.is_resolver) def department_dashboard(request): - return render(request, "dashboard/department_dashboard.html") \ No newline at end of file + departments = request.user.departments.all() + issues = Issue.objects.filter(department__in=departments).order_by('-created_at') + return render(request, "dashboard/department_dashboard.html", {"issues": issues, "departments": departments}) + +@login_required +@user_passes_test(lambda u: u.is_resolver) +def update_issue_status(request, issue_id): + issue = get_object_or_404(Issue, id=issue_id) + + # Make sure the user belongs to the department assigned to the issue + if issue.department not in request.user.departments.all(): + return redirect("department_dashboard") + + if request.method == "POST": + new_status = request.POST.get("status") + if new_status in [Issue.STATUS_IN_PROGRESS, Issue.STATUS_RESOLVED]: + issue.status = new_status + issue.save() + return redirect("department_dashboard") \ No newline at end of file diff --git a/civicfix/run_server.py b/civicfix/run_server.py new file mode 100644 index 0000000..ed0e0c9 --- /dev/null +++ b/civicfix/run_server.py @@ -0,0 +1,5 @@ +from waitress import serve +from civicfix.wsgi import application + +if __name__ == "__main__": + serve(application, host="10.1.192.152", port=8000)