diff --git a/.gitignore b/.gitignore index 29f259eb6c56168f6900de126a51d7fd1182825e..3c376992c637b36d09a851de4354488e043144a6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ +# Ignore all __pycache__ directories __pycache__/ -**/migrations/* -!**/migrations/__init__.py -registration \ No newline at end of file + +# Ignore all migrations directories +migrations/ diff --git a/MisplaceAI/Dockerfile b/MisplaceAI/Dockerfile index 2751409fb116ec1e36e1c09abb413496254d0516..bbeaee31e95f39b4c64319e63cc11104bae8b118 100644 --- a/MisplaceAI/Dockerfile +++ b/MisplaceAI/Dockerfile @@ -1,3 +1,4 @@ +#Dockerfile # Use the full Python runtime as a parent image FROM python:3.10 diff --git a/MisplaceAI/admin_app/templates/admin_app/admin_dashboard.html b/MisplaceAI/admin_app/templates/admin_app/admin_dashboard.html index 93e33016877f4b90d7ed59c09ea2c0f782bd1892..7d43f1d78e51f10211c0937ac166315995fa8649 100644 --- a/MisplaceAI/admin_app/templates/admin_app/admin_dashboard.html +++ b/MisplaceAI/admin_app/templates/admin_app/admin_dashboard.html @@ -10,7 +10,10 @@ <h1>Admin Dashboard</h1> <div class="list-group"> <a href="{% url 'admin_users' %}" class="list-group-item list-group-item-action">Users Activity</a> - <!-- Add other admin links here --> + <a href="{% url 'rules:list_rules' %}" class="list-group-item list-group-item-action">Manage Rules</a> + <a href="{% url 'rules:admin_add_location' %}" class="list-group-item list-group-item-action">Add + Location</a> + <a href="{% url 'rules:admin_add_item' %}" class="list-group-item list-group-item-action">Add Item</a> </div> </div> </div> diff --git a/MisplaceAI/authentication/__pycache__/apps.cpython-310.pyc b/MisplaceAI/authentication/__pycache__/apps.cpython-310.pyc index 3d7bbc9a78b073b047dfd0b96999c880a622acb1..aac04fde8475c476d4a28433344a6abb5dedec2d 100644 Binary files a/MisplaceAI/authentication/__pycache__/apps.cpython-310.pyc and b/MisplaceAI/authentication/__pycache__/apps.cpython-310.pyc differ diff --git a/MisplaceAI/docker-compose.yml b/MisplaceAI/docker-compose.yml index 216683c1bf4279a4e93031fe8b045b130b6bdbc0..dc72d67c1858994e704915f2609af6280f52ef7b 100644 --- a/MisplaceAI/docker-compose.yml +++ b/MisplaceAI/docker-compose.yml @@ -1,3 +1,4 @@ +#docker-compose.yml version: '3.7' services: diff --git a/MisplaceAI/rules/__pycache__/__init__.cpython-39.pyc b/MisplaceAI/rules/__pycache__/__init__.cpython-39.pyc deleted file mode 100644 index cf5573c4ac264229497c6125f1ed0780162d5844..0000000000000000000000000000000000000000 Binary files a/MisplaceAI/rules/__pycache__/__init__.cpython-39.pyc and /dev/null differ diff --git a/MisplaceAI/rules/__pycache__/admin.cpython-39.pyc b/MisplaceAI/rules/__pycache__/admin.cpython-39.pyc deleted file mode 100644 index 1bf9591e9ac3cc1c8238693e6444b34c1696be78..0000000000000000000000000000000000000000 Binary files a/MisplaceAI/rules/__pycache__/admin.cpython-39.pyc and /dev/null differ diff --git a/MisplaceAI/rules/__pycache__/apps.cpython-39.pyc b/MisplaceAI/rules/__pycache__/apps.cpython-39.pyc deleted file mode 100644 index 31706469dbe44504ed6dba139fb1d69414c59785..0000000000000000000000000000000000000000 Binary files a/MisplaceAI/rules/__pycache__/apps.cpython-39.pyc and /dev/null differ diff --git a/MisplaceAI/rules/__pycache__/forms.cpython-310.pyc b/MisplaceAI/rules/__pycache__/forms.cpython-310.pyc index 7417812a7a9b27f25d270f78b952272824883522..48868c69e39a330151c6a798a8488645e623b8dc 100644 Binary files a/MisplaceAI/rules/__pycache__/forms.cpython-310.pyc and b/MisplaceAI/rules/__pycache__/forms.cpython-310.pyc differ diff --git a/MisplaceAI/rules/__pycache__/models.cpython-310.pyc b/MisplaceAI/rules/__pycache__/models.cpython-310.pyc index 2824feb030c55b134bd72336e3f50b46a45a196a..8b2afb9c30caa32b45e9aa9b932de5cb8dc483e2 100644 Binary files a/MisplaceAI/rules/__pycache__/models.cpython-310.pyc and b/MisplaceAI/rules/__pycache__/models.cpython-310.pyc differ diff --git a/MisplaceAI/rules/__pycache__/models.cpython-39.pyc b/MisplaceAI/rules/__pycache__/models.cpython-39.pyc deleted file mode 100644 index 3f0a1653bc660437e4e5e302b81d9b463a2ebb6f..0000000000000000000000000000000000000000 Binary files a/MisplaceAI/rules/__pycache__/models.cpython-39.pyc and /dev/null differ diff --git a/MisplaceAI/rules/__pycache__/urls.cpython-310.pyc b/MisplaceAI/rules/__pycache__/urls.cpython-310.pyc index 0afb21aa3fd21a6317c48e6bf1b3afcfbcfb2731..9b94bafd72cfdc090116bbe497c908634a18faf0 100644 Binary files a/MisplaceAI/rules/__pycache__/urls.cpython-310.pyc and b/MisplaceAI/rules/__pycache__/urls.cpython-310.pyc differ diff --git a/MisplaceAI/rules/__pycache__/utils.cpython-310.pyc b/MisplaceAI/rules/__pycache__/utils.cpython-310.pyc deleted file mode 100644 index 6ec72ae2621dab37e52edba017451b6382a1ca8f..0000000000000000000000000000000000000000 Binary files a/MisplaceAI/rules/__pycache__/utils.cpython-310.pyc and /dev/null differ diff --git a/MisplaceAI/rules/__pycache__/views.cpython-310.pyc b/MisplaceAI/rules/__pycache__/views.cpython-310.pyc index 5175198a3ed3629c01840fa93443e5fea63fd89f..35a893392adfc093578df244f706a42561585443 100644 Binary files a/MisplaceAI/rules/__pycache__/views.cpython-310.pyc and b/MisplaceAI/rules/__pycache__/views.cpython-310.pyc differ diff --git a/MisplaceAI/rules/forms.py b/MisplaceAI/rules/forms.py index f45d52468eb3cb9823bc73b92dd9435e8e77cc37..22600146eeafb4da313d26cfc8a761d3ca93b358 100644 --- a/MisplaceAI/rules/forms.py +++ b/MisplaceAI/rules/forms.py @@ -1,25 +1,17 @@ from django import forms -from .models import Rule +from .models import Rule, Location, Item class RuleForm(forms.ModelForm): class Meta: model = Rule - fields = ['name', 'condition', 'action'] # Include all the fields you want from the model + fields = ['user', 'item', 'locations'] - def clean_name(self): - name = self.cleaned_data.get('name') - if not name: - raise forms.ValidationError("The name field cannot be left blank.") - return name - - def clean_condition(self): - condition = self.cleaned_data.get('condition') - if not condition: - raise forms.ValidationError("The condition field cannot be left blank.") - return condition +class LocationForm(forms.ModelForm): + class Meta: + model = Location + fields = ['name'] - def clean_action(self): - action = self.cleaned_data.get('action') - if not action: - raise forms.ValidationError("The action field cannot be left blank.") - return action +class ItemForm(forms.ModelForm): + class Meta: + model = Item + fields = ['name'] diff --git a/MisplaceAI/rules/migrations/0001_initial.py b/MisplaceAI/rules/migrations/0001_initial.py index 30eb8303022e50ceaebfeb0f4f5b45349b8d7329..c88616d13dc5931656d8064a1534232f6d91f4a8 100644 --- a/MisplaceAI/rules/migrations/0001_initial.py +++ b/MisplaceAI/rules/migrations/0001_initial.py @@ -1,6 +1,8 @@ -# Generated by Django 3.2 on 2024-05-15 15:06 +# Generated by Django 3.2 on 2024-05-15 20:43 +from django.conf import settings from django.db import migrations, models +import django.db.models.deletion class Migration(migrations.Migration): @@ -8,16 +10,31 @@ class Migration(migrations.Migration): initial = True dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), ] operations = [ migrations.CreateModel( - name='Rule', + name='Item', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=255, unique=True)), + ], + ), + migrations.CreateModel( + name='Location', fields=[ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('name', models.CharField(max_length=255, unique=True)), - ('condition', models.TextField()), - ('action', models.TextField()), + ], + ), + migrations.CreateModel( + name='Rule', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('item', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='rules.item')), + ('locations', models.ManyToManyField(to='rules.Location')), + ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), ], ), ] diff --git a/MisplaceAI/rules/migrations/__pycache__/0001_initial.cpython-310.pyc b/MisplaceAI/rules/migrations/__pycache__/0001_initial.cpython-310.pyc index 3cbc07602d64ef4ef2f9a6599d6144f132f80e71..41f9305df4cc8cb517a5b241eaef71e4d84b52e9 100644 Binary files a/MisplaceAI/rules/migrations/__pycache__/0001_initial.cpython-310.pyc and b/MisplaceAI/rules/migrations/__pycache__/0001_initial.cpython-310.pyc differ diff --git a/MisplaceAI/rules/models.py b/MisplaceAI/rules/models.py index a11306e1e6a6eb4b42093cc11828aef78a30d7ae..cef49b1c41239d330ce3d638fa3ef9c90c690140 100644 --- a/MisplaceAI/rules/models.py +++ b/MisplaceAI/rules/models.py @@ -1,9 +1,22 @@ from django.db import models +from django.contrib.auth.models import User -class Rule(models.Model): +class Location(models.Model): + name = models.CharField(max_length=255, unique=True) + + def __str__(self): + return self.name + +class Item(models.Model): name = models.CharField(max_length=255, unique=True) - condition = models.TextField() - action = models.TextField() def __str__(self): return self.name + +class Rule(models.Model): + user = models.ForeignKey(User, on_delete=models.CASCADE) + item = models.ForeignKey(Item, on_delete=models.CASCADE) + locations = models.ManyToManyField(Location) + + def __str__(self): + return f"{self.item.name} Rule" diff --git a/MisplaceAI/rules/templates/rules/add_rule.html b/MisplaceAI/rules/templates/rules/add_rule.html index 078583c7036c5094e31b133e36381bcbbc924f1f..66bcc05a71ddf7da4e63b612e7722b5fed29f17a 100644 --- a/MisplaceAI/rules/templates/rules/add_rule.html +++ b/MisplaceAI/rules/templates/rules/add_rule.html @@ -1,11 +1,15 @@ -<h1>Add Rule</h1> -<form method="post"> - {% csrf_token %} - <label for="name">Name:</label> - <input type="text" id="name" name="name"><br> - <label for="condition">Condition:</label> - <textarea id="condition" name="condition"></textarea><br> - <label for="action">Action:</label> - <textarea id="action" name="action"></textarea><br> - <input type="submit" value="Submit"> -</form> \ No newline at end of file +{% extends 'core/base.html' %} +{% load static %} + +{% block title %}Add Rule{% endblock %} + +{% block content %} +<div class="container mt-5"> + <h1>Add Rule</h1> + <form method="post"> + {% csrf_token %} + {{ form.as_p }} + <button type="submit" class="btn btn-success">Save Rule</button> + </form> +</div> +{% endblock %} \ No newline at end of file diff --git a/MisplaceAI/rules/templates/rules/admin_add_item.html b/MisplaceAI/rules/templates/rules/admin_add_item.html new file mode 100644 index 0000000000000000000000000000000000000000..39aca1d60fe26326c2c8322ccaa7088342c2c7f6 --- /dev/null +++ b/MisplaceAI/rules/templates/rules/admin_add_item.html @@ -0,0 +1,58 @@ +{% extends 'core/base.html' %} +{% load static %} + +{% block title %}Add Item{% endblock %} + +{% block content %} +<div class="container mt-5"> + <h1>Add Item</h1> + <form method="post"> + {% csrf_token %} + {{ form.as_p }} + <button type="submit" class="btn btn-success">Add Item</button> + </form> + + <h2>Existing Items</h2> + <table class="table"> + <thead> + <tr> + <th>Item Name</th> + <th>Actions</th> + </tr> + </thead> + <tbody> + {% for item in page_obj %} + <tr> + <td>{{ item.name }}</td> + <td> + <a href="{% url 'rules:edit_item' item.id %}" class="btn btn-warning btn-sm">Edit</a> + <a href="{% url 'rules:delete_item' item.id %}" class="btn btn-danger btn-sm" + onclick="return confirm('Are you sure you want to delete this item?');">Delete</a> + </td> + </tr> + {% empty %} + <tr> + <td colspan="2">No items found.</td> + </tr> + {% endfor %} + </tbody> + </table> + + <!-- Pagination --> + <nav aria-label="Page navigation"> + <ul class="pagination"> + {% if page_obj.has_previous %} + <li class="page-item"><a class="page-link" href="?page={{ page_obj.previous_page_number }}">Previous</a> + </li> + {% endif %} + {% for num in page_obj.paginator.page_range %} + <li class="page-item {% if page_obj.number == num %}active{% endif %}"><a class="page-link" + href="?page={{ num }}">{{ num }}</a></li> + {% endfor %} + {% if page_obj.has_next %} + <li class="page-item"><a class="page-link" href="?page={{ page_obj.next_page_number }}">Next</a></li> + {% endif %} + </ul> + </nav> +</div> +{% endblock %} \ No newline at end of file diff --git a/MisplaceAI/rules/templates/rules/admin_add_location.html b/MisplaceAI/rules/templates/rules/admin_add_location.html new file mode 100644 index 0000000000000000000000000000000000000000..2df1d82c363b1c9b2b5a36217f60ef6e8b04e7f7 --- /dev/null +++ b/MisplaceAI/rules/templates/rules/admin_add_location.html @@ -0,0 +1,57 @@ +{% extends 'core/base.html' %} +{% load static %} + +{% block title %}Add Location{% endblock %} + +{% block content %} +<div class="container mt-5"> + <h1>Add Location</h1> + <form method="post"> + {% csrf_token %} + {{ form.as_p }} + <button type="submit" class="btn btn-success">Add Location</button> + </form> + + <h2>Existing Locations</h2> + <table class="table"> + <thead> + <tr> + <th>Location Name</th> + <th>Actions</th> + </tr> + </thead> + <tbody> + {% for location in page_obj %} + <tr> + <td>{{ location.name }}</td> + <td> + <a href="{% url 'rules:edit_location' location.id %}" class="btn btn-warning btn-sm">Edit</a> + <a href="{% url 'rules:delete_location' location.id %}" class="btn btn-danger btn-sm" + onclick="return confirm('Are you sure you want to delete this location?');">Delete</a> + </td> + </tr> + {% empty %} + <tr> + <td colspan="2">No locations found.</td> + </tr> + {% endfor %} + </tbody> + </table> + <!-- Pagination --> + <nav aria-label="Page navigation"> + <ul class="pagination"> + {% if page_obj.has_previous %} + <li class="page-item"><a class="page-link" href="?page={{ page_obj.previous_page_number }}">Previous</a> + </li> + {% endif %} + {% for num in page_obj.paginator.page_range %} + <li class="page-item {% if page_obj.number == num %}active{% endif %}"><a class="page-link" + href="?page={{ num }}">{{ num }}</a></li> + {% endfor %} + {% if page_obj.has_next %} + <li class="page-item"><a class="page-link" href="?page={{ page_obj.next_page_number }}">Next</a></li> + {% endif %} + </ul> + </nav> +</div> +{% endblock %} \ No newline at end of file diff --git a/MisplaceAI/rules/templates/rules/confirm_delete.html b/MisplaceAI/rules/templates/rules/confirm_delete.html index 6e2fa104232c0c01d20467a11e83fe68e6a11ad7..f1ea7515fbe3408fd4cd1cee45a69d878d732bf2 100644 --- a/MisplaceAI/rules/templates/rules/confirm_delete.html +++ b/MisplaceAI/rules/templates/rules/confirm_delete.html @@ -1,18 +1,13 @@ -<!DOCTYPE html> -<html lang="en"> +{% extends 'core/base.html' %} +{% block title %}Delete Rule{% endblock %} -<head> - <meta charset="UTF-8"> - <title>Delete Rule</title> -</head> - -<body> +{% block content %} +<div class="container mt-5"> <h1>Are you sure you want to delete "{{ rule.name }}"?</h1> <form method="post"> {% csrf_token %} - <input type="submit" value="Confirm Delete"> - <a href="{% url 'rules:list_rules' %}">Cancel</a> + <button type="submit" class="btn btn-danger">Confirm Delete</button> + <a href="{% url 'rules:list_rules' %}" class="btn btn-secondary">Cancel</a> </form> -</body> - -</html> \ No newline at end of file +</div> +{% endblock %} \ No newline at end of file diff --git a/MisplaceAI/rules/templates/rules/confirm_delete_item.html b/MisplaceAI/rules/templates/rules/confirm_delete_item.html new file mode 100644 index 0000000000000000000000000000000000000000..ae879879480825b32d50f09092b6db8093df7c00 --- /dev/null +++ b/MisplaceAI/rules/templates/rules/confirm_delete_item.html @@ -0,0 +1,15 @@ +{% extends 'core/base.html' %} +{% load static %} + +{% block title %}Confirm Delete{% endblock %} + +{% block content %} +<div class="container mt-5"> + <h1>Are you sure you want to delete "{{ item.name }}"?</h1> + <form method="post"> + {% csrf_token %} + <button type="submit" class="btn btn-danger">Delete</button> + <a href="{% url 'rules:admin_add_item' %}" class="btn btn-secondary">Cancel</a> + </form> +</div> +{% endblock %} \ No newline at end of file diff --git a/MisplaceAI/rules/templates/rules/confirm_delete_location.html b/MisplaceAI/rules/templates/rules/confirm_delete_location.html new file mode 100644 index 0000000000000000000000000000000000000000..5a2665ce38f10d45c01d6fdf14b2a38a1ee6aed6 --- /dev/null +++ b/MisplaceAI/rules/templates/rules/confirm_delete_location.html @@ -0,0 +1,17 @@ +{% extends 'core/base.html' %} +{% load static %} + +{% block title %}Confirm Delete{% endblock %} + +{% block content %} +<div class="container mt-5"> + <h1>Delete Location</h1> + <p>Are you sure you want to delete "{{ location.name }}"?</p> + + <form method="post"> + {% csrf_token %} + <button type="submit" class="btn btn-danger">Delete</button> + <a href="{% url 'rules:admin_add_location' %}" class="btn btn-secondary">Cancel</a> + </form> +</div> +{% endblock %} \ No newline at end of file diff --git a/MisplaceAI/rules/templates/rules/edit_item.html b/MisplaceAI/rules/templates/rules/edit_item.html new file mode 100644 index 0000000000000000000000000000000000000000..9960b9400c48f1f01babddb5f41edcd9f8a822fc --- /dev/null +++ b/MisplaceAI/rules/templates/rules/edit_item.html @@ -0,0 +1,16 @@ +{% extends 'core/base.html' %} +{% load static %} + +{% block title %}Edit Item{% endblock %} + +{% block content %} +<div class="container mt-5"> + <h1>Edit Item</h1> + <form method="post"> + {% csrf_token %} + {{ form.as_p }} + <button type="submit" class="btn btn-success">Save Changes</button> + <a href="{% url 'rules:admin_add_item' %}" class="btn btn-secondary">Cancel</a> + </form> +</div> +{% endblock %} \ No newline at end of file diff --git a/MisplaceAI/rules/templates/rules/edit_location.html b/MisplaceAI/rules/templates/rules/edit_location.html new file mode 100644 index 0000000000000000000000000000000000000000..a772b200d4faf56f0b94a1dba3ae3a9dc76b927a --- /dev/null +++ b/MisplaceAI/rules/templates/rules/edit_location.html @@ -0,0 +1,16 @@ +{% extends 'core/base.html' %} +{% load static %} + +{% block title %}Edit Location{% endblock %} + +{% block content %} +<div class="container mt-5"> + <h1>Edit Location</h1> + <form method="post" novalidate> + {% csrf_token %} + {{ form.as_p }} + <button type="submit" class="btn btn-primary">Update</button> + <a href="{% url 'rules:admin_add_location' %}" class="btn btn-secondary">Cancel</a> + </form> +</div> +{% endblock %} \ No newline at end of file diff --git a/MisplaceAI/rules/templates/rules/list_rules.html b/MisplaceAI/rules/templates/rules/list_rules.html index 2be2586c9c74fe0b553a56139b4834c11ed49d3e..9ecc344b37424c8f1d4b4dc778ed631929d15af4 100644 --- a/MisplaceAI/rules/templates/rules/list_rules.html +++ b/MisplaceAI/rules/templates/rules/list_rules.html @@ -1,28 +1,44 @@ -<!DOCTYPE html> -<html lang="en"> +{% extends 'core/base.html' %} +{% load static %} -<head> - <meta charset="UTF-8"> - <title>List of Rules</title> -</head> +{% block title %}List of Rules{% endblock %} -<body> +{% block content %} +<div class="container mt-5"> <h1>List of Rules</h1> - <ul> - {% for rule in rules %} - <li> - {{ rule.name }} - {% if rule.name %} - : <a href="{% url 'rules:get_rule' rule.name %}">View</a> - | <a href="{% url 'rules:update_rule' rule.name %}">Edit</a> - | <a href="{% url 'rules:remove_rule' rule.name %}">Delete</a> - {% else %} - : Name unavailable - {% endif %} - </li> - {% endfor %} - </ul> - <a href="{% url 'rules:add_rule' %}">Add New Rule</a> -</body> - -</html> \ No newline at end of file + <table class="table"> + <thead> + <tr> + <th>Item</th> + <th>Locations</th> + <th>Actions</th> + </tr> + </thead> + <tbody> + {% for rule in rules %} + <tr> + <td>{{ rule.item.name }}</td> + <td> + {% for location in rule.locations.all %} + {{ location.name }}{% if not forloop.last %}, {% endif %} + {% endfor %} + </td> + <td> + <a href="{% url 'rules:get_rule' rule.id %}" class="btn btn-info">View</a> + <a href="{% url 'rules:update_rule' rule.id %}" class="btn btn-warning">Edit</a> + <form action="{% url 'rules:remove_rule' rule.id %}" method="post" style="display:inline;"> + {% csrf_token %} + <button type="submit" class="btn btn-danger">Delete</button> + </form> + </td> + </tr> + {% empty %} + <tr> + <td colspan="3">No rules available.</td> + </tr> + {% endfor %} + </tbody> + </table> + <a href="{% url 'rules:add_rule' %}" class="btn btn-primary">Add New Rule</a> +</div> +{% endblock %} \ No newline at end of file diff --git a/MisplaceAI/rules/templates/rules/rule_detail.html b/MisplaceAI/rules/templates/rules/rule_detail.html index 5b352318b21d927aa6fbb4ab608a451b2002b65d..da291d5eaa7b9a6c6f4d8868d0ccd1059fceceed 100644 --- a/MisplaceAI/rules/templates/rules/rule_detail.html +++ b/MisplaceAI/rules/templates/rules/rule_detail.html @@ -1,4 +1,18 @@ -<h1>Rule Details</h1> -<p><strong>Name:</strong> {{ rule.name }}</p> -<p><strong>Condition:</strong> {{ rule.condition }}</p> -<p><strong>Action:</strong> {{ rule.action }}</p> \ No newline at end of file +{% extends 'core/base.html' %} +{% load static %} + +{% block title %}Rule Detail{% endblock %} + +{% block content %} +<div class="container mt-5"> + <h1>Rule Detail</h1> + <p><strong>Item:</strong> {{ rule.item.name }}</p> + <p><strong>Locations:</strong> {% for location in rule.locations.all %}{{ location.name }}{% if not forloop.last %}, + {% endif %}{% endfor %}</p> + <a href="{% url 'rules:update_rule' rule.id %}" class="btn btn-warning">Edit</a> + <form action="{% url 'rules:remove_rule' rule.id %}" method="post" style="display:inline;"> + {% csrf_token %} + <button type="submit" class="btn btn-danger">Delete</button> + </form> +</div> +{% endblock %} \ No newline at end of file diff --git a/MisplaceAI/rules/templates/rules/update_rule.html b/MisplaceAI/rules/templates/rules/update_rule.html index 0a5843eab3c23a2142a42d717c7df01921112130..4d3ca50f65d31c13ab5a11175a7d49950029e303 100644 --- a/MisplaceAI/rules/templates/rules/update_rule.html +++ b/MisplaceAI/rules/templates/rules/update_rule.html @@ -1,6 +1,15 @@ -<h1>Update Rule</h1> -<form method="post"> - {% csrf_token %} - {{ form.as_p }} - <button type="submit">Save changes</button> -</form> \ No newline at end of file +{% extends 'core/base.html' %} +{% load static %} + +{% block title %}Update Rule{% endblock %} + +{% block content %} +<div class="container mt-5"> + <h1>Update Rule</h1> + <form method="post"> + {% csrf_token %} + {{ form.as_p }} + <button type="submit" class="btn btn-success">Save Changes</button> + </form> +</div> +{% endblock %} \ No newline at end of file diff --git a/MisplaceAI/rules/urls.py b/MisplaceAI/rules/urls.py index a556df62dcd008059419ee8964c1453ea39695bd..345f00c78a7b65548f21dfc13bd4c59ec077c2e3 100644 --- a/MisplaceAI/rules/urls.py +++ b/MisplaceAI/rules/urls.py @@ -1,12 +1,23 @@ from django.urls import path -from .views import list_rules, add_rule, get_rule, remove_rule, update_rule +from .views import ( + list_rules, add_rule, get_rule, remove_rule, update_rule, + admin_add_location, admin_add_item, edit_item, delete_item,edit_location, delete_location +) app_name = 'rules' urlpatterns = [ path('', list_rules, name='list_rules'), path('add/', add_rule, name='add_rule'), - path('<str:rule_name>/', get_rule, name='get_rule'), - path('remove/<str:rule_name>/', remove_rule, name='remove_rule'), - path('update/<str:rule_name>/', update_rule, name='update_rule'), + path('<int:rule_id>/', get_rule, name='get_rule'), + path('remove/<int:rule_id>/', remove_rule, name='remove_rule'), + path('update/<int:rule_id>/', update_rule, name='update_rule'), + path('admin_add_location/', admin_add_location, name='admin_add_location'), + path('admin_add_item/', admin_add_item, name='admin_add_item'), + + path('edit_item/<int:item_id>/', edit_item, name='edit_item'), + path('delete_item/<int:item_id>/', delete_item, name='delete_item'), + path('edit_location/<int:location_id>/', edit_location, name='edit_location'), + path('delete_location/<int:location_id>/', delete_location, name='delete_location'), + ] diff --git a/MisplaceAI/rules/utils.py b/MisplaceAI/rules/utils.py index fd8fbcd06d573a50bfef2137d6d32160a45bb52f..3de4b76f0db17f871d44b4184f9b7cc284f950a6 100644 --- a/MisplaceAI/rules/utils.py +++ b/MisplaceAI/rules/utils.py @@ -1,40 +1,38 @@ +from django.core.exceptions import ValidationError from .models import Rule -class RulesManager: - def __init__(self): - self.rules = self.load_rules() +def validate_rule(rule): + """ + Validates a rule to ensure there are no conflicting rules. + """ + existing_rules = Rule.objects.filter(item=rule.item).exclude(id=rule.id) + for existing_rule in existing_rules: + if set(existing_rule.locations.all()) & set(rule.locations.all()): + raise ValidationError(f"A rule for {rule.item.name} already exists for one or more of the selected locations.") + +def check_item_location(item, location): + """ + Checks if the given item is allowed at the given location based on the existing rules. + """ + rules = Rule.objects.filter(item=item, locations=location) + if not rules.exists(): + return False + return True - def load_rules(self): - """Load the rules from the database.""" - return {rule.name: {'condition': rule.condition, 'action': rule.action} for rule in Rule.objects.all()} +def get_item_location_rules(item): + """ + Retrieves all the locations where the given item is allowed based on the existing rules. + """ + rules = Rule.objects.filter(item=item) + allowed_locations = set() + for rule in rules: + allowed_locations.update(rule.locations.all()) + return allowed_locations - def save_rule(self, name, condition, action): - """Save a rule to the database.""" - rule, created = Rule.objects.update_or_create(name=name, defaults={'condition': condition, 'action': action}) - self.rules[name] = {'condition': condition, 'action': action} - - def add_rule(self, rule): - """Add a new rule to the database.""" - self.save_rule(rule["name"], rule["condition"], rule["action"]) - - def get_rule(self, rule_name): - """Retrieve a rule by its name.""" - rule = self.rules.get(rule_name) - if not rule: - try: - rule_obj = Rule.objects.get(name=rule_name) - rule = {'condition': rule_obj.condition, 'action': rule_obj.action} - self.rules[rule_name] = rule - except Rule.DoesNotExist: - return None - return rule - - def remove_rule(self, rule_name): - """Remove a rule by its name.""" - if rule_name in self.rules: - del self.rules[rule_name] - Rule.objects.filter(name=rule_name).delete() - - def list_rules(self): - """List all rules.""" - return list(self.rules.values()) +def notify_user_of_misplacement(user, item, location): + """ + Notifies the user that the given item is misplaced at the given location. + """ + # This function can be implemented to send notifications to the user. + # For now, we will just print a message. + print(f"User {user.username}, the item '{item.name}' is misplaced at '{location.name}'.") diff --git a/MisplaceAI/rules/views.py b/MisplaceAI/rules/views.py index 9ef587236a10a2358de64098abf0f1b4533776a1..a97a2e73d5574ffcd190c38d6d2eb11371a3489c 100644 --- a/MisplaceAI/rules/views.py +++ b/MisplaceAI/rules/views.py @@ -1,7 +1,11 @@ from django.shortcuts import render, redirect, get_object_or_404 -from django.http import JsonResponse -from .models import Rule -from .forms import RuleForm +from django.contrib.auth.decorators import login_required +from django.contrib import messages +from .models import Rule, Location, Item +from .forms import RuleForm, LocationForm, ItemForm +from django.core.paginator import Paginator + +from django.urls import reverse def list_rules(request): rules = Rule.objects.all() @@ -13,40 +17,108 @@ def add_rule(request): if form.is_valid(): form.save() return redirect('rules:list_rules') - else: - return render(request, 'rules/add_rule.html', {'form': form}) else: - form = RuleForm() # An unbound form for GET requests - return render(request, 'rules/add_rule.html', {'form': form}) + form = RuleForm() + return render(request, 'rules/add_rule.html', {'form': form}) -def get_rule(request, rule_name): - rule = get_object_or_404(Rule, name=rule_name) +def get_rule(request, rule_id): + rule = get_object_or_404(Rule, id=rule_id) return render(request, 'rules/rule_detail.html', {'rule': rule}) -def remove_rule(request, rule_name): - rule = get_object_or_404(Rule, name=rule_name) +def remove_rule(request, rule_id): + rule = get_object_or_404(Rule, id=rule_id) if request.method == 'POST': rule.delete() return redirect('rules:list_rules') return render(request, 'rules/confirm_delete.html', {'rule': rule}) -def update_rule(request, rule_name): - rule = get_object_or_404(Rule, name=rule_name) +def update_rule(request, rule_id): + rule = get_object_or_404(Rule, id=rule_id) if request.method == 'POST': form = RuleForm(request.POST, instance=rule) if form.is_valid(): form.save() - return redirect('rules:get_rule', rule_name=rule.name) - else: - return render(request, 'rules/update_rule.html', {'form': form, 'rule': rule}) + return redirect('rules:get_rule', rule_id=rule.id) else: form = RuleForm(instance=rule) - return render(request, 'rules/update_rule.html', {'form': form, 'rule': rule}) + return render(request, 'rules/update_rule.html', {'form': form, 'rule': rule}) +@login_required +def admin_add_location(request): + locations = Location.objects.all().order_by('name') + paginator = Paginator(locations, 10) # Show 10 locations per page + page_number = request.GET.get('page') + page_obj = paginator.get_page(page_number) -def remove_rule(request, rule_name): - rule = get_object_or_404(Rule, name=rule_name) if request.method == 'POST': - rule.delete() - return redirect('rules:list_rules') - return render(request, 'rules/confirm_delete.html', {'rule': rule}) + form = LocationForm(request.POST) + if form.is_valid(): + form.save() + messages.success(request, 'Location added successfully.') + return redirect('rules:admin_add_location') + else: + form = LocationForm() + return render(request, 'rules/admin_add_location.html', {'form': form, 'page_obj': page_obj}) + + +@login_required +def admin_add_item(request): + items = Item.objects.all().order_by('name') + paginator = Paginator(items, 10) # Show 10 items per page + page_number = request.GET.get('page') + page_obj = paginator.get_page(page_number) + + if request.method == 'POST': + form = ItemForm(request.POST) + if form.is_valid(): + form.save() + messages.success(request, 'Item added successfully.') + return redirect('rules:admin_add_item') + else: + form = ItemForm() + return render(request, 'rules/admin_add_item.html', {'form': form, 'page_obj': page_obj}) + + + +def edit_item(request, item_id): + item = get_object_or_404(Item, id=item_id) + if request.method == 'POST': + form = ItemForm(request.POST, instance=item) + if form.is_valid(): + form.save() + return redirect('rules:admin_add_item') + else: + form = ItemForm(instance=item) + return render(request, 'rules/edit_item.html', {'form': form}) + +def delete_item(request, item_id): + item = get_object_or_404(Item, id=item_id) + if request.method == 'POST': + item.delete() + return redirect('rules:admin_add_item') + return render(request, 'rules/confirm_delete_item.html', {'item': item}) + + + + + +def edit_location(request, location_id): + location = get_object_or_404(Location, id=location_id) + if request.method == 'POST': + form = LocationForm(request.POST, instance=location) + if form.is_valid(): + form.save() + # Use the 'reverse' to correctly use named URLs with namespaces + return redirect(reverse('rules:admin_add_location')) + else: + form = LocationForm(instance=location) + return render(request, 'rules/edit_location.html', {'form': form}) + + + +def delete_location(request, location_id): + location = get_object_or_404(Location, id=location_id) + if request.method == 'POST': + location.delete() + return redirect('rules:admin_add_location') + return render(request, 'rules/confirm_delete_location.html', {'location': location}) diff --git a/README.md b/README.md index e57dc36037f526a8d0f6f9003d7938c6b8a69558..bb3e3734ebe1bdb2068f80e6bcc725ca6b0a0b7c 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,84 @@ # Identification of Misplaced Items +## DATABASE + +### Connect though terminal + +```bash +docker exec -it misplaceai-db-1 mysql -u root -p +``` +<br> +Then enter password + +```bash +Enter password: rootpassword +``` + +<br> +Select Database: +```bash +mysql> USE misplaceai; +``` + +### Drop all tables + +Disable foreign key checks: + +``bash +SET FOREIGN_KEY_CHECKS = 0; +``` + +Generate and execute the drop script: + +```bash +SET @tables = NULL; +SELECT GROUP_CONCAT('`', table_name, '`') INTO @tables +FROM information_schema.tables +WHERE table_schema = (SELECT DATABASE()); + +SET @tables = CONCAT('DROP TABLE IF EXISTS ', @tables); +PREPARE stmt FROM @tables; +EXECUTE stmt; +DEALLOCATE PREPARE stmt; + +``` + +Enable foreign key checks: + +```bash +SET FOREIGN_KEY_CHECKS = 1; + +``` + +Verify that all tables are dropped: + +```sql +SHOW TABLES; +``` + +exir + +```bash +mysql> EXIT; +Bye +``` + + +## +migrations +```bash +docker-compose exec web python manage.py makemigrations + docker-compose exec web python manage.py migrate +``` + +create superuser: + +``` bash + + docker-compose exec web python manage.py createsuperusers +``` + ## Getting started