diff --git a/MisplaceAI/.env b/MisplaceAI/.env new file mode 100644 index 0000000000000000000000000000000000000000..4942f2e869c6aa74bc87fe14919eee000c34c276 --- /dev/null +++ b/MisplaceAI/.env @@ -0,0 +1,13 @@ +# Django settings +DJANGO_SECRET_KEY=your-secret-key +DJANGO_DEBUG=True +DJANGO_ALLOWED_HOSTS=localhost,127.0.0.1 + +# Database settings +MYSQL_DATABASE=misplaceai +MYSQL_USER=user +MYSQL_PASSWORD=password +MYSQL_HOST=db +MYSQL_PORT=3306 + +# Other environment variables can go here as needed diff --git a/MisplaceAI/.gitignore b/MisplaceAI/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..a4537bc1a36c360ab38e5b7b4210248a6f39cfc8 --- /dev/null +++ b/MisplaceAI/.gitignore @@ -0,0 +1,3 @@ +__pycache__/ +**/migrations/* +!**/migrations/__init__.py diff --git a/MisplaceAI/Dockerfile b/MisplaceAI/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..2751409fb116ec1e36e1c09abb413496254d0516 --- /dev/null +++ b/MisplaceAI/Dockerfile @@ -0,0 +1,28 @@ +# Use the full Python runtime as a parent image +FROM python:3.10 + +# Set the working directory in the container +WORKDIR /app + +# Copy the current directory contents into the container at /app +COPY . /app + +# Install system dependencies +RUN apt-get update && apt-get install -y --no-install-recommends \ + build-essential \ + libpq-dev \ + libblas-dev \ + liblapack-dev \ + libatlas-base-dev \ + && rm -rf /var/lib/apt/lists/* + +# Install Python dependencies +RUN pip install --no-cache-dir -r requirements.txt + +# Expose port 8000 for the Django app +EXPOSE 8000 + +# Run the Django server +CMD ["python", "manage.py", "runserver", "0.0.0.0:8000"] + + diff --git a/MisplaceAI/MisplaceAI/__init__.py b/MisplaceAI/MisplaceAI/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/MisplaceAI/MisplaceAI/__pycache__/__init__.cpython-310.pyc b/MisplaceAI/MisplaceAI/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..078d38eb0b2f9da48d54dafc99a299f50e6a6d41 Binary files /dev/null and b/MisplaceAI/MisplaceAI/__pycache__/__init__.cpython-310.pyc differ diff --git a/MisplaceAI/MisplaceAI/__pycache__/__init__.cpython-39.pyc b/MisplaceAI/MisplaceAI/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..39514c299e2d96eba915cae5ecd252866f8493fb Binary files /dev/null and b/MisplaceAI/MisplaceAI/__pycache__/__init__.cpython-39.pyc differ diff --git a/MisplaceAI/MisplaceAI/__pycache__/settings.cpython-310.pyc b/MisplaceAI/MisplaceAI/__pycache__/settings.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b2fb8276f5548c1496a1d219646b6814fa3f04da Binary files /dev/null and b/MisplaceAI/MisplaceAI/__pycache__/settings.cpython-310.pyc differ diff --git a/MisplaceAI/MisplaceAI/__pycache__/settings.cpython-39.pyc b/MisplaceAI/MisplaceAI/__pycache__/settings.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..bd1fcc2fe9624981c3f5eb4b052d9f815ce76c9d Binary files /dev/null and b/MisplaceAI/MisplaceAI/__pycache__/settings.cpython-39.pyc differ diff --git a/MisplaceAI/MisplaceAI/__pycache__/urls.cpython-310.pyc b/MisplaceAI/MisplaceAI/__pycache__/urls.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..824207fc5694350a0be5912936807940285d2970 Binary files /dev/null and b/MisplaceAI/MisplaceAI/__pycache__/urls.cpython-310.pyc differ diff --git a/MisplaceAI/MisplaceAI/__pycache__/urls.cpython-39.pyc b/MisplaceAI/MisplaceAI/__pycache__/urls.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ca21d865f36b19dcd688c5f867e9b328b683660d Binary files /dev/null and b/MisplaceAI/MisplaceAI/__pycache__/urls.cpython-39.pyc differ diff --git a/MisplaceAI/MisplaceAI/__pycache__/wsgi.cpython-310.pyc b/MisplaceAI/MisplaceAI/__pycache__/wsgi.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b278dc80a278b11c6c991a62d86ae58e828dba23 Binary files /dev/null and b/MisplaceAI/MisplaceAI/__pycache__/wsgi.cpython-310.pyc differ diff --git a/MisplaceAI/MisplaceAI/__pycache__/wsgi.cpython-39.pyc b/MisplaceAI/MisplaceAI/__pycache__/wsgi.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c9509d845c199b794b0e7c9fafe1ee38151d0b04 Binary files /dev/null and b/MisplaceAI/MisplaceAI/__pycache__/wsgi.cpython-39.pyc differ diff --git a/MisplaceAI/MisplaceAI/asgi.py b/MisplaceAI/MisplaceAI/asgi.py new file mode 100644 index 0000000000000000000000000000000000000000..b5ced38b218574be7e42fb876b9fe8255af5af4e --- /dev/null +++ b/MisplaceAI/MisplaceAI/asgi.py @@ -0,0 +1,16 @@ +""" +ASGI config for MisplaceAI project. + +It exposes the ASGI callable as a module-level variable named ``application``. + +For more information on this file, see +https://docs.djangoproject.com/en/4.0/howto/deployment/asgi/ +""" + +import os + +from django.core.asgi import get_asgi_application + +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'MisplaceAI.settings') + +application = get_asgi_application() diff --git a/MisplaceAI/MisplaceAI/settings.py b/MisplaceAI/MisplaceAI/settings.py new file mode 100644 index 0000000000000000000000000000000000000000..a445cdaa398143a9af2c49665f012110390c728e --- /dev/null +++ b/MisplaceAI/MisplaceAI/settings.py @@ -0,0 +1,143 @@ +# misplaceai/settings.py +""" +Django settings for MisplaceAI project. + +Generated by 'django-admin startproject' using Django 4.0.2. + +For more information on this file, see +https://docs.djangoproject.com/en/4.0/topics/settings/ + +For the full list of settings and their values, see +https://docs.djangoproject.com/en/4.0/ref/settings/ +""" + +from pathlib import Path + +# Build paths inside the project like this: BASE_DIR / 'subdir'. +BASE_DIR = Path(__file__).resolve().parent.parent + + +# Quick-start development settings - unsuitable for production +# See https://docs.djangoproject.com/en/4.0/howto/deployment/checklist/ + +# SECURITY WARNING: keep the secret key used in production secret! +SECRET_KEY = 'django-insecure-34gsb7^)t!ltchc4#2^sn_#+$(4i=ts107$(2yjy#ung+%mrn2' + +# SECURITY WARNING: don't run with debug turned on in production! +DEBUG = True + +ALLOWED_HOSTS = [] + + + + + +# Application definition + +INSTALLED_APPS = [ + 'django.contrib.admin', + 'django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'django.contrib.messages', + 'django.contrib.staticfiles', + 'rules.apps.RulesConfig', + 'authentication.apps.AuthenticationConfig', + 'core.apps.CoreConfig', + +] + +MIDDLEWARE = [ + 'django.middleware.security.SecurityMiddleware', + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.middleware.common.CommonMiddleware', + 'django.middleware.csrf.CsrfViewMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'django.contrib.messages.middleware.MessageMiddleware', + 'django.middleware.clickjacking.XFrameOptionsMiddleware', +] + +ROOT_URLCONF = 'MisplaceAI.urls' + +TEMPLATES = [ + { + 'BACKEND': 'django.template.backends.django.DjangoTemplates', + 'DIRS': [], + 'APP_DIRS': True, + 'OPTIONS': { + 'context_processors': [ + 'django.template.context_processors.debug', + 'django.template.context_processors.request', + 'django.contrib.auth.context_processors.auth', + 'django.contrib.messages.context_processors.messages', + ], + }, + }, +] + +WSGI_APPLICATION = 'MisplaceAI.wsgi.application' + + +# Database +# https://docs.djangoproject.com/en/4.0/ref/settings/#databases + +DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.mysql', + 'NAME': 'misplaceai', + 'USER': 'user', + 'PASSWORD': 'password', + 'HOST': 'db', # Use service name defined in docker-compose.yml + 'PORT': '3306', + } +} + + +# Password validation +# https://docs.djangoproject.com/en/4.0/ref/settings/#auth-password-validators + +AUTH_PASSWORD_VALIDATORS = [ + { + 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', + 'OPTIONS': { + 'min_length': 8, + } + }, + { + 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', + }, +] + + +# Internationalization +# https://docs.djangoproject.com/en/4.0/topics/i18n/ + +LANGUAGE_CODE = 'en-us' + +TIME_ZONE = 'UTC' + +USE_I18N = True + +USE_TZ = True + + +# Static files (CSS, JavaScript, Images) +# https://docs.djangoproject.com/en/4.0/howto/static-files/ +STATIC_URL = '/static/' +STATICFILES_DIRS = [ + BASE_DIR / "static", + BASE_DIR / "authentication/static", +] + + + +# Default primary key field type +# https://docs.djangoproject.com/en/4.0/ref/settings/#default-auto-field + +DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' diff --git a/MisplaceAI/MisplaceAI/urls.py b/MisplaceAI/MisplaceAI/urls.py new file mode 100644 index 0000000000000000000000000000000000000000..7603c75a46a7e63ea3132bbccf693facce5cd6a3 --- /dev/null +++ b/MisplaceAI/MisplaceAI/urls.py @@ -0,0 +1,10 @@ +from django.contrib import admin +from django.urls import path, include + +urlpatterns = [ + path('admin/', admin.site.urls), + # path('detection/', include('detection.urls')), + path('rules/', include('rules.urls')), + path('auth/', include('authentication.urls')), + path('', include('core.urls')), +] diff --git a/MisplaceAI/MisplaceAI/wsgi.py b/MisplaceAI/MisplaceAI/wsgi.py new file mode 100644 index 0000000000000000000000000000000000000000..9c90901b43a3a77debd3518f1549383be0aa8e10 --- /dev/null +++ b/MisplaceAI/MisplaceAI/wsgi.py @@ -0,0 +1,16 @@ +""" +WSGI config for MisplaceAI project. + +It exposes the WSGI callable as a module-level variable named ``application``. + +For more information on this file, see +https://docs.djangoproject.com/en/4.0/howto/deployment/wsgi/ +""" + +import os + +from django.core.wsgi import get_wsgi_application + +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'MisplaceAI.settings') + +application = get_wsgi_application() diff --git a/MisplaceAI/authentication/__init__.py b/MisplaceAI/authentication/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/MisplaceAI/authentication/__pycache__/__init__.cpython-310.pyc b/MisplaceAI/authentication/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..017e4bdf241bb7481d92ba991272552173d611a9 Binary files /dev/null and b/MisplaceAI/authentication/__pycache__/__init__.cpython-310.pyc differ diff --git a/MisplaceAI/authentication/__pycache__/admin.cpython-310.pyc b/MisplaceAI/authentication/__pycache__/admin.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..dac94ce94b5a58d1b5f8e701ed95f2e27c9f3dcf Binary files /dev/null and b/MisplaceAI/authentication/__pycache__/admin.cpython-310.pyc differ diff --git a/MisplaceAI/authentication/__pycache__/apps.cpython-310.pyc b/MisplaceAI/authentication/__pycache__/apps.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3d7bbc9a78b073b047dfd0b96999c880a622acb1 Binary files /dev/null and b/MisplaceAI/authentication/__pycache__/apps.cpython-310.pyc differ diff --git a/MisplaceAI/authentication/__pycache__/forms.cpython-310.pyc b/MisplaceAI/authentication/__pycache__/forms.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..54a0fe0e954d3449bd542c7c9fb000510ea1a227 Binary files /dev/null and b/MisplaceAI/authentication/__pycache__/forms.cpython-310.pyc differ diff --git a/MisplaceAI/authentication/__pycache__/models.cpython-310.pyc b/MisplaceAI/authentication/__pycache__/models.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8c15e23552ddbd9dd2781f899dc8b825c73916fd Binary files /dev/null and b/MisplaceAI/authentication/__pycache__/models.cpython-310.pyc differ diff --git a/MisplaceAI/authentication/__pycache__/urls.cpython-310.pyc b/MisplaceAI/authentication/__pycache__/urls.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..184fa57b1b865e05ab0820c3e8c10e2a3499ea50 Binary files /dev/null and b/MisplaceAI/authentication/__pycache__/urls.cpython-310.pyc differ diff --git a/MisplaceAI/authentication/__pycache__/views.cpython-310.pyc b/MisplaceAI/authentication/__pycache__/views.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ad850b7d1b4592afc569793dfbc107f5ec30cfa8 Binary files /dev/null and b/MisplaceAI/authentication/__pycache__/views.cpython-310.pyc differ diff --git a/MisplaceAI/authentication/admin.py b/MisplaceAI/authentication/admin.py new file mode 100644 index 0000000000000000000000000000000000000000..8c38f3f3dad51e4585f3984282c2a4bec5349c1e --- /dev/null +++ b/MisplaceAI/authentication/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/MisplaceAI/authentication/apps.py b/MisplaceAI/authentication/apps.py new file mode 100644 index 0000000000000000000000000000000000000000..924a9cc0de98bd0072f270a125adcb21b1be37e6 --- /dev/null +++ b/MisplaceAI/authentication/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + +class AuthenticationConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'authentication' diff --git a/MisplaceAI/authentication/forms.py b/MisplaceAI/authentication/forms.py new file mode 100644 index 0000000000000000000000000000000000000000..c589bc6ad7da6bedca284c81597571d9a48fe8f8 --- /dev/null +++ b/MisplaceAI/authentication/forms.py @@ -0,0 +1,29 @@ +from django import forms +from django.contrib.auth.forms import UserCreationForm, AuthenticationForm +from django.contrib.auth.models import User + +class RegisterForm(UserCreationForm): + email = forms.EmailField() + + class Meta: + model = User + fields = ['username', 'email', 'password1', 'password2'] + +class LoginForm(AuthenticationForm): + username = forms.CharField(label='Username') + password = forms.CharField(widget=forms.PasswordInput) + + +class CustomUserCreationForm(UserCreationForm): + email = forms.EmailField(required=True, widget=forms.EmailInput(attrs={'class': 'form-control'})) + + class Meta: + model = User + fields = ("username", "email", "password1", "password2") + + def save(self, commit=True): + user = super(CustomUserCreationForm, self).save(commit=False) + user.email = self.cleaned_data["email"] + if commit: + user.save() + return user \ No newline at end of file diff --git a/MisplaceAI/authentication/migrations/__init__.py b/MisplaceAI/authentication/migrations/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/MisplaceAI/authentication/migrations/__pycache__/__init__.cpython-310.pyc b/MisplaceAI/authentication/migrations/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9c4a76f0095b0ff00c141a39653ff241428bc140 Binary files /dev/null and b/MisplaceAI/authentication/migrations/__pycache__/__init__.cpython-310.pyc differ diff --git a/MisplaceAI/authentication/models.py b/MisplaceAI/authentication/models.py new file mode 100644 index 0000000000000000000000000000000000000000..71a836239075aa6e6e4ecb700e9c42c95c022d91 --- /dev/null +++ b/MisplaceAI/authentication/models.py @@ -0,0 +1,3 @@ +from django.db import models + +# Create your models here. diff --git a/MisplaceAI/authentication/static/authentication/js/registration.js b/MisplaceAI/authentication/static/authentication/js/registration.js new file mode 100644 index 0000000000000000000000000000000000000000..ebd1822bea8d3240f0f9fb51aaf296fbdaa281c5 --- /dev/null +++ b/MisplaceAI/authentication/static/authentication/js/registration.js @@ -0,0 +1,64 @@ +document.addEventListener('DOMContentLoaded', function () { + const password1 = document.querySelector('#id_password1'); + const password2 = document.querySelector('#id_password2'); + const togglePassword1 = document.querySelector('#togglePassword1'); + const togglePassword2 = document.querySelector('#togglePassword2'); + const passwordMatchMessage = document.querySelector('#passwordMatchMessage'); + + togglePassword1.addEventListener('click', function () { + const type = password1.type === 'password' ? 'text' : 'password'; + password1.type = type; + this.classList.toggle('fa-eye'); + this.classList.toggle('fa-eye-slash'); + }); + + togglePassword2.addEventListener('click', function () { + const type = password2.type === 'password' ? 'text' : 'password'; + password2.type = type; + this.classList.toggle('fa-eye'); + this.classList.toggle('fa-eye-slash'); + }); + + // Password requirements validation + const requirements = { + length: { regex: /.{8,}/, element: document.querySelector('#passwordHelpBlock li:nth-child(1)') }, + uppercase: { regex: /[A-Z]/, element: document.querySelector('#passwordHelpBlock li:nth-child(2)') }, + lowercase: { regex: /[a-z]/, element: document.querySelector('#passwordHelpBlock li:nth-child(3)') }, + number: { regex: /[0-9]/, element: document.querySelector('#passwordHelpBlock li:nth-child(4)') } + }; + + function validatePassword() { + const value = password1.value; + let allValid = true; + for (const requirement in requirements) { + if (requirements[requirement].regex.test(value)) { + requirements[requirement].element.classList.add('text-success'); + requirements[requirement].element.classList.remove('text-muted'); + } else { + requirements[requirement].element.classList.remove('text-success'); + requirements[requirement].element.classList.add('text-muted'); + allValid = false; + } + } + return allValid; + } + + function checkPasswordMatch() { + if (validatePassword()) { + if (password1.value === password2.value) { + passwordMatchMessage.textContent = 'Passwords match'; + passwordMatchMessage.classList.remove('text-muted', 'text-danger'); + passwordMatchMessage.classList.add('text-success'); + } else { + passwordMatchMessage.textContent = 'Passwords do not match'; + passwordMatchMessage.classList.remove('text-muted', 'text-success'); + passwordMatchMessage.classList.add('text-danger'); + } + } else { + passwordMatchMessage.textContent = ''; + } + } + + password1.addEventListener('input', validatePassword); + password2.addEventListener('input', checkPasswordMatch); +}); diff --git a/MisplaceAI/authentication/templates/authentication/login.html b/MisplaceAI/authentication/templates/authentication/login.html new file mode 100644 index 0000000000000000000000000000000000000000..d723eade60abb91cb0d028053bfd5612db77a70b --- /dev/null +++ b/MisplaceAI/authentication/templates/authentication/login.html @@ -0,0 +1,36 @@ +{% extends 'core/base.html' %} + +{% block title %}Login{% endblock %} + +{% block content %} +<div class="container mt-5"> + <div class="row justify-content-center"> + <div class="col-md-6"> + <div class="card"> + <div class="card-header"> + <h3 class="text-center">Login</h3> + </div> + <div class="card-body"> + <form method="post"> + {% csrf_token %} + <div class="form-group"> + <label for="id_username">Username</label> + {{ form.username }} + </div> + <div class="form-group"> + <label for="id_password">Password</label> + {{ form.password }} + </div> + <div class="form-group text-center"> + <button type="submit" class="btn btn-primary btn-block">Login</button> + </div> + </form> + </div> + <div class="card-footer text-center"> + <small>Don't have an account? <a href="{% url 'register' %}">Register here</a></small> + </div> + </div> + </div> + </div> +</div> +{% endblock %} \ No newline at end of file diff --git a/MisplaceAI/authentication/templates/authentication/logout.html b/MisplaceAI/authentication/templates/authentication/logout.html new file mode 100644 index 0000000000000000000000000000000000000000..c3c30b2d299e90a12fdd94aed0f22b5e58f9a047 --- /dev/null +++ b/MisplaceAI/authentication/templates/authentication/logout.html @@ -0,0 +1,11 @@ +{% extends 'core/base.html' %} + +{% block title %}Logout{% endblock %} + +{% block content %} +<h2>Are you sure you want to logout?</h2> +<form method="post"> + {% csrf_token %} + <button type="submit">Logout</button> +</form> +{% endblock %} \ No newline at end of file diff --git a/MisplaceAI/authentication/templates/authentication/register.html b/MisplaceAI/authentication/templates/authentication/register.html new file mode 100644 index 0000000000000000000000000000000000000000..c8b4694553afa51333320bad1b4640d8fdebd038 --- /dev/null +++ b/MisplaceAI/authentication/templates/authentication/register.html @@ -0,0 +1,86 @@ +{% extends 'core/base.html' %} +{% load static %} +{% block title %}Register{% endblock %} + +{% block content %} +<div class="container mt-5"> + <div class="row justify-content-center"> + <div class="col-md-6"> + <div class="card"> + <div class="card-header"> + <h3 class="text-center">Register</h3> + </div> + <div class="card-body"> + <form method="post" id="registration-form"> + {% csrf_token %} + {% if form.errors %} + <div class="alert alert-danger"> + <ul> + {% for field in form %} + {% for error in field.errors %} + <li>{{ error }}</li> + {% endfor %} + {% endfor %} + {% for error in form.non_field_errors %} + <li>{{ error }}</li> + {% endfor %} + </ul> + </div> + {% endif %} + <div class="form-group"> + <label for="id_username">Username</label> + {{ form.username }} + </div> + <div class="form-group"> + <label for="id_email">Email</label> + {{ form.email }} + </div> + <div class="form-group position-relative"> + <label for="id_password1">Password</label> + <div class="input-group"> + {{ form.password1 }} + <div class="input-group-append"> + <span class="input-group-text"> + <i class="fa fa-eye" id="togglePassword1" style="cursor: pointer;"></i> + </span> + </div> + </div> + <small id="passwordHelpBlock" class="form-text text-muted"> + Your password must meet the following requirements: + <ul> + <li>At least 8 characters</li> + <li>At least one uppercase letter</li> + <li>At least one lowercase letter</li> + <li>At least one number</li> + </ul> + </small> + </div> + <div class="form-group position-relative"> + <label for="id_password2">Confirm Password</label> + <div class="input-group"> + {{ form.password2 }} + <div class="input-group-append"> + <span class="input-group-text"> + <i class="fa fa-eye" id="togglePassword2" style="cursor: pointer;"></i> + </span> + </div> + </div> + <small id="passwordMatchMessage" class="form-text text-muted"></small> + </div> + <div class="form-group text-center"> + <button type="submit" class="btn btn-primary btn-block">Register</button> + </div> + </form> + </div> + <div class="card-footer text-center"> + <small>Already have an account? <a href="{% url 'login' %}">Login here</a></small> + </div> + </div> + </div> + </div> +</div> +{% endblock %} + +{% block extra_js %} +<script src="{% static 'authentication/js/registration.js' %}"></script> +{% endblock %} \ No newline at end of file diff --git a/MisplaceAI/authentication/tests.py b/MisplaceAI/authentication/tests.py new file mode 100644 index 0000000000000000000000000000000000000000..7ce503c2dd97ba78597f6ff6e4393132753573f6 --- /dev/null +++ b/MisplaceAI/authentication/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/MisplaceAI/authentication/urls.py b/MisplaceAI/authentication/urls.py new file mode 100644 index 0000000000000000000000000000000000000000..ff3c6245a031c3180e360d83de905d08a93f8289 --- /dev/null +++ b/MisplaceAI/authentication/urls.py @@ -0,0 +1,8 @@ +from django.urls import path +from .views import register_view, login_view, logout_view + +urlpatterns = [ + path('register/', register_view, name='register'), + path('login/', login_view, name='login'), + path('logout/', logout_view, name='logout'), +] diff --git a/MisplaceAI/authentication/views.py b/MisplaceAI/authentication/views.py new file mode 100644 index 0000000000000000000000000000000000000000..b45f8b96d0ece934d3f82b77aef2d3d531a9e694 --- /dev/null +++ b/MisplaceAI/authentication/views.py @@ -0,0 +1,49 @@ +from django.shortcuts import render, redirect +from django.contrib.auth import login, authenticate, logout +from django.contrib.auth.forms import AuthenticationForm, UserCreationForm +from .forms import RegisterForm, LoginForm +from .forms import CustomUserCreationForm +def register_view(request): + if request.method == 'POST': + form = CustomUserCreationForm(request.POST) + if form.is_valid(): + user = form.save() + login(request, user) + return redirect('home') + else: + form = CustomUserCreationForm() + + # Add Bootstrap classes to form fields + form.fields['username'].widget.attrs.update({'class': 'form-control'}) + form.fields['email'].widget.attrs.update({'class': 'form-control'}) + form.fields['password1'].widget.attrs.update({'class': 'form-control'}) + form.fields['password2'].widget.attrs.update({'class': 'form-control'}) + + return render(request, 'authentication/register.html', {'form': form}) + + +def login_view(request): + if request.method == 'POST': + form = AuthenticationForm(request, data=request.POST) + if form.is_valid(): + username = form.cleaned_data.get('username') + password = form.cleaned_data.get('password') + user = authenticate(username=username, password=password) + if user is not None: + login(request, user) + return redirect('home') + else: + form = AuthenticationForm() + + # Add Bootstrap classes to form fields + form.fields['username'].widget.attrs.update({'class': 'form-control'}) + form.fields['password'].widget.attrs.update({'class': 'form-control'}) + + return render(request, 'authentication/login.html', {'form': form}) + + +def logout_view(request): + if request.method == 'POST': + logout(request) + return redirect('login') + return render(request, 'authentication/logout.html') \ No newline at end of file diff --git a/MisplaceAI/core/__init__.py b/MisplaceAI/core/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/MisplaceAI/core/__pycache__/__init__.cpython-310.pyc b/MisplaceAI/core/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9a6cc05a9f2f9243c326a74d3dd83339163e3305 Binary files /dev/null and b/MisplaceAI/core/__pycache__/__init__.cpython-310.pyc differ diff --git a/MisplaceAI/core/__pycache__/admin.cpython-310.pyc b/MisplaceAI/core/__pycache__/admin.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..71ba20137e12e7da54b297f9c2f89e7ac555f52e Binary files /dev/null and b/MisplaceAI/core/__pycache__/admin.cpython-310.pyc differ diff --git a/MisplaceAI/core/__pycache__/apps.cpython-310.pyc b/MisplaceAI/core/__pycache__/apps.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..93cc4d30411ee392704c2ab4e350c5cdcbb12a32 Binary files /dev/null and b/MisplaceAI/core/__pycache__/apps.cpython-310.pyc differ diff --git a/MisplaceAI/core/__pycache__/models.cpython-310.pyc b/MisplaceAI/core/__pycache__/models.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..fa61354c3319e28acd74b5ca588900e26b257778 Binary files /dev/null and b/MisplaceAI/core/__pycache__/models.cpython-310.pyc differ diff --git a/MisplaceAI/core/__pycache__/urls.cpython-310.pyc b/MisplaceAI/core/__pycache__/urls.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8f202e656e5806acf4e4d41b19da4ed61228bf9a Binary files /dev/null and b/MisplaceAI/core/__pycache__/urls.cpython-310.pyc differ diff --git a/MisplaceAI/core/__pycache__/views.cpython-310.pyc b/MisplaceAI/core/__pycache__/views.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..aa105ff2feb5e2260633aab16d392395d62eb820 Binary files /dev/null and b/MisplaceAI/core/__pycache__/views.cpython-310.pyc differ diff --git a/MisplaceAI/core/admin.py b/MisplaceAI/core/admin.py new file mode 100644 index 0000000000000000000000000000000000000000..8c38f3f3dad51e4585f3984282c2a4bec5349c1e --- /dev/null +++ b/MisplaceAI/core/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/MisplaceAI/core/apps.py b/MisplaceAI/core/apps.py new file mode 100644 index 0000000000000000000000000000000000000000..8115ae60bc647249211ecbd4bbf6aa65478e9b5c --- /dev/null +++ b/MisplaceAI/core/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class CoreConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'core' diff --git a/MisplaceAI/core/migrations/__init__.py b/MisplaceAI/core/migrations/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/MisplaceAI/core/migrations/__pycache__/__init__.cpython-310.pyc b/MisplaceAI/core/migrations/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e8eda8e962a61d1c433f852036d60dfeb04f99ce Binary files /dev/null and b/MisplaceAI/core/migrations/__pycache__/__init__.cpython-310.pyc differ diff --git a/MisplaceAI/core/models.py b/MisplaceAI/core/models.py new file mode 100644 index 0000000000000000000000000000000000000000..71a836239075aa6e6e4ecb700e9c42c95c022d91 --- /dev/null +++ b/MisplaceAI/core/models.py @@ -0,0 +1,3 @@ +from django.db import models + +# Create your models here. diff --git a/MisplaceAI/core/templates/core/base.html b/MisplaceAI/core/templates/core/base.html new file mode 100644 index 0000000000000000000000000000000000000000..10a9baad27c0a1de50883f288beaa4723349a419 --- /dev/null +++ b/MisplaceAI/core/templates/core/base.html @@ -0,0 +1,21 @@ +<!DOCTYPE html> +<html lang="en"> + +<head> + <meta charset="UTF-8"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <title>{% block title %}MisplaceAI{% endblock %}</title> + {% include 'core/head_includes.html' %} + {% block extra_head %}{% endblock %} +</head> + +<body> + {% include 'core/navbar.html' %} + <div class="container"> + {% block content %}{% endblock %} + </div> + {% block extra_js %}{% endblock %} + +</body> + +</html> \ No newline at end of file diff --git a/MisplaceAI/core/templates/core/head_includes.html b/MisplaceAI/core/templates/core/head_includes.html new file mode 100644 index 0000000000000000000000000000000000000000..558bc0d36b964f6dfed90f6f7223d936e9d55b39 --- /dev/null +++ b/MisplaceAI/core/templates/core/head_includes.html @@ -0,0 +1,7 @@ +<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css"> +<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css"> + + +<script src="https://code.jquery.com/jquery-3.5.1.slim.min.js"></script> +<script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.9.3/dist/umd/popper.min.js"></script> +<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"></script> \ No newline at end of file diff --git a/MisplaceAI/core/templates/core/home.html b/MisplaceAI/core/templates/core/home.html new file mode 100644 index 0000000000000000000000000000000000000000..4058e85f3c3bc2362a673e81b323ccbfe7db3aa1 --- /dev/null +++ b/MisplaceAI/core/templates/core/home.html @@ -0,0 +1,8 @@ +{% extends 'core/base.html' %} + +{% block title %}Home{% endblock %} + +{% block content %} +<h1>Welcome to MisplaceAI</h1> +<p>This is the home page of the MisplaceAI application.</p> +{% endblock %} \ No newline at end of file diff --git a/MisplaceAI/core/templates/core/navbar.html b/MisplaceAI/core/templates/core/navbar.html new file mode 100644 index 0000000000000000000000000000000000000000..4f586968c051c59cb083b1e7567ceffc74bac25b --- /dev/null +++ b/MisplaceAI/core/templates/core/navbar.html @@ -0,0 +1,29 @@ +<nav class="navbar navbar-expand-lg navbar-light bg-light"> + <a class="navbar-brand" href="{% url 'home' %}">MisplaceAI</a> + <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNav" + aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation"> + <span class="navbar-toggler-icon"></span> + </button> + <div class="collapse navbar-collapse" id="navbarNav"> + <ul class="navbar-nav ml-auto"> + <li class="nav-item"> + <a class="nav-link" href="{% url 'home' %}">Home</a> + </li> + {% if user.is_authenticated %} + <li class="nav-item"> + <form method="post" action="{% url 'logout' %}" class="form-inline"> + {% csrf_token %} + <button type="submit" class="btn btn-link nav-link" style="padding: 0;">Logout</button> + </form> + </li> + {% else %} + <li class="nav-item"> + <a class="nav-link" href="{% url 'login' %}">Login</a> + </li> + <li class="nav-item"> + <a class="nav-link" href="{% url 'register' %}">Register</a> + </li> + {% endif %} + </ul> + </div> +</nav> \ No newline at end of file diff --git a/MisplaceAI/core/tests.py b/MisplaceAI/core/tests.py new file mode 100644 index 0000000000000000000000000000000000000000..7ce503c2dd97ba78597f6ff6e4393132753573f6 --- /dev/null +++ b/MisplaceAI/core/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/MisplaceAI/core/urls.py b/MisplaceAI/core/urls.py new file mode 100644 index 0000000000000000000000000000000000000000..049a4a57471032d0c3309b5e36c61f24256a2263 --- /dev/null +++ b/MisplaceAI/core/urls.py @@ -0,0 +1,6 @@ +from django.urls import path +from .views import home_view + +urlpatterns = [ + path('', home_view, name='home'), +] diff --git a/MisplaceAI/core/views.py b/MisplaceAI/core/views.py new file mode 100644 index 0000000000000000000000000000000000000000..0cecdb1b905af367d7eea4d1131e1f8d5856848d --- /dev/null +++ b/MisplaceAI/core/views.py @@ -0,0 +1,4 @@ +from django.shortcuts import render + +def home_view(request): + return render(request, 'core/home.html') diff --git a/MisplaceAI/db.sqlite3 b/MisplaceAI/db.sqlite3 new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/MisplaceAI/docker-compose.yml b/MisplaceAI/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..216683c1bf4279a4e93031fe8b045b130b6bdbc0 --- /dev/null +++ b/MisplaceAI/docker-compose.yml @@ -0,0 +1,25 @@ +version: '3.7' + +services: + web: + build: . + command: python manage.py runserver 0.0.0.0:8000 + volumes: + - .:/app + ports: + - "8000:8000" + depends_on: + - db + + db: + image: mysql:5.7 + environment: + MYSQL_DATABASE: misplaceai + MYSQL_USER: user + MYSQL_PASSWORD: password + MYSQL_ROOT_PASSWORD: rootpassword + volumes: + - mysql_data:/var/lib/mysql + +volumes: + mysql_data: diff --git a/MisplaceAI/docker-web-entrypoint.sh b/MisplaceAI/docker-web-entrypoint.sh new file mode 100644 index 0000000000000000000000000000000000000000..6bfacd73fb860908479959459949cfb527592d00 --- /dev/null +++ b/MisplaceAI/docker-web-entrypoint.sh @@ -0,0 +1,21 @@ +#!/bin/bash + +echo "Waiting for MySQL to be available" +max_attempts=30 +count=0 +while ! mysqladmin ping -h"$DATABASE_HOST" --silent; do + count=$((count+1)) + if [ $count -ge $max_attempts ]; then + echo "ERROR: MySQL not available after $max_attempts attempts" + exit 1 + fi + sleep 1 +done +echo "MySQL is available and ready" + +# Run Django migrations +python manage.py migrate + +# Execute the command passed to this script +echo "Executing command: $@" +exec "$@" diff --git a/MisplaceAI/manage.py b/MisplaceAI/manage.py new file mode 100644 index 0000000000000000000000000000000000000000..9b7c10dbeffa5e715fcd6ca1d7f88794f9a9a398 --- /dev/null +++ b/MisplaceAI/manage.py @@ -0,0 +1,22 @@ +#!/usr/bin/env python +"""Django's command-line utility for administrative tasks.""" +import os +import sys + + +def main(): + """Run administrative tasks.""" + os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'MisplaceAI.settings') + try: + from django.core.management import execute_from_command_line + except ImportError as exc: + raise ImportError( + "Couldn't import Django. Are you sure it's installed and " + "available on your PYTHONPATH environment variable? Did you " + "forget to activate a virtual environment?" + ) from exc + execute_from_command_line(sys.argv) + + +if __name__ == '__main__': + main() diff --git a/MisplaceAI/requirements.txt b/MisplaceAI/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..cc29688aa50c8efb5cf6da2c59df7104700b3bc6 --- /dev/null +++ b/MisplaceAI/requirements.txt @@ -0,0 +1,50 @@ +# # TensorFlow and essential machine learning libraries +# tensorflow==2.10.1 +# tensorflow-addons==0.22.0 +# tensorflow-datasets==4.9.0 +# tensorflow-estimator==2.10.0 +# tensorflow-hub==0.16.1 +# tensorflow-io==0.31.0 +# tensorflow-io-gcs-filesystem==0.31.0 +# tensorflow-metadata==1.13.0 +# tensorflow-model-optimization==0.8.0 +# tensorflow-object-detection-api==0.1.1 +# tensorflow-text==2.10.0 + +# # Other essential libraries +# numpy==1.24.4 +# pandas==2.2.0 +# scikit-learn==1.4.1.post1 +# scipy==1.12.0 +# matplotlib==3.8.3 +# seaborn==0.12.2 +# pillow==10.2.0 +# opencv-python==4.9.0.80 +# h5py==3.10.0 +# protobuf==3.19.6 + +# # Additional useful libraries +# absl-py==1.4.0 +# six==1.16.0 +# wrapt==1.16.0 +# termcolor==2.4.0 +# tqdm==4.66.2 +# requests==2.31.0 + +# # Flask for web applications +# flask==2.2.5 + +# # PyYAML for configuration files +# pyyaml==6.0 + +# # Essential Python utilities +# ipython==8.15.0 +# setuptools==68.2.2 +# wheel==0.41.2 + +## +Django==3.2 +mysqlclient==2.1.0 + + + diff --git a/MisplaceAI/rules/__init__.py b/MisplaceAI/rules/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/MisplaceAI/rules/__pycache__/__init__.cpython-310.pyc b/MisplaceAI/rules/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7c4ce198000f324a5cc3cfbd2b0a16952a95ba4e Binary files /dev/null and b/MisplaceAI/rules/__pycache__/__init__.cpython-310.pyc differ diff --git a/MisplaceAI/rules/__pycache__/__init__.cpython-39.pyc b/MisplaceAI/rules/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..cf5573c4ac264229497c6125f1ed0780162d5844 Binary files /dev/null and b/MisplaceAI/rules/__pycache__/__init__.cpython-39.pyc differ diff --git a/MisplaceAI/rules/__pycache__/admin.cpython-310.pyc b/MisplaceAI/rules/__pycache__/admin.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b5013b1ba1b0ae3a96269e865e48d81bc01ef099 Binary files /dev/null and b/MisplaceAI/rules/__pycache__/admin.cpython-310.pyc differ diff --git a/MisplaceAI/rules/__pycache__/admin.cpython-39.pyc b/MisplaceAI/rules/__pycache__/admin.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1bf9591e9ac3cc1c8238693e6444b34c1696be78 Binary files /dev/null and b/MisplaceAI/rules/__pycache__/admin.cpython-39.pyc differ diff --git a/MisplaceAI/rules/__pycache__/apps.cpython-310.pyc b/MisplaceAI/rules/__pycache__/apps.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0b7ecebdbaaabc1b1d67e235fb902e6c9eeee389 Binary files /dev/null and b/MisplaceAI/rules/__pycache__/apps.cpython-310.pyc differ diff --git a/MisplaceAI/rules/__pycache__/apps.cpython-39.pyc b/MisplaceAI/rules/__pycache__/apps.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6520755d5f9651200136cc640dbc0a6f74f0d904 Binary files /dev/null and b/MisplaceAI/rules/__pycache__/apps.cpython-39.pyc differ diff --git a/MisplaceAI/rules/__pycache__/forms.cpython-310.pyc b/MisplaceAI/rules/__pycache__/forms.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7417812a7a9b27f25d270f78b952272824883522 Binary files /dev/null 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 new file mode 100644 index 0000000000000000000000000000000000000000..ead1e8c47db6a427658e77d0ddc21653b3388760 Binary files /dev/null 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 new file mode 100644 index 0000000000000000000000000000000000000000..3f0a1653bc660437e4e5e302b81d9b463a2ebb6f Binary files /dev/null and b/MisplaceAI/rules/__pycache__/models.cpython-39.pyc differ diff --git a/MisplaceAI/rules/__pycache__/urls.cpython-310.pyc b/MisplaceAI/rules/__pycache__/urls.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7704261dde59232899a80cb1f727c50b2b5ad46f Binary files /dev/null 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 new file mode 100644 index 0000000000000000000000000000000000000000..6ec72ae2621dab37e52edba017451b6382a1ca8f Binary files /dev/null and b/MisplaceAI/rules/__pycache__/utils.cpython-310.pyc differ diff --git a/MisplaceAI/rules/__pycache__/views.cpython-310.pyc b/MisplaceAI/rules/__pycache__/views.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..cd89d7edaaf8a661c4e0e1a21b9d143d4661a98e Binary files /dev/null and b/MisplaceAI/rules/__pycache__/views.cpython-310.pyc differ diff --git a/MisplaceAI/rules/admin.py b/MisplaceAI/rules/admin.py new file mode 100644 index 0000000000000000000000000000000000000000..8c38f3f3dad51e4585f3984282c2a4bec5349c1e --- /dev/null +++ b/MisplaceAI/rules/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/MisplaceAI/rules/apps.py b/MisplaceAI/rules/apps.py new file mode 100644 index 0000000000000000000000000000000000000000..ed6347665e5ff869df35cffa07e88a70d7e4ffe5 --- /dev/null +++ b/MisplaceAI/rules/apps.py @@ -0,0 +1,7 @@ +from django.apps import AppConfig + +class RulesConfig(AppConfig): + name = 'rules' + verbose_name = 'Rules Management' # Optional: human-readable name for the app + + diff --git a/MisplaceAI/rules/forms.py b/MisplaceAI/rules/forms.py new file mode 100644 index 0000000000000000000000000000000000000000..f45d52468eb3cb9823bc73b92dd9435e8e77cc37 --- /dev/null +++ b/MisplaceAI/rules/forms.py @@ -0,0 +1,25 @@ +from django import forms +from .models import Rule + +class RuleForm(forms.ModelForm): + class Meta: + model = Rule + fields = ['name', 'condition', 'action'] # Include all the fields you want from the model + + 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 + + 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 diff --git a/MisplaceAI/rules/migrations/0001_initial.py b/MisplaceAI/rules/migrations/0001_initial.py new file mode 100644 index 0000000000000000000000000000000000000000..30eb8303022e50ceaebfeb0f4f5b45349b8d7329 --- /dev/null +++ b/MisplaceAI/rules/migrations/0001_initial.py @@ -0,0 +1,23 @@ +# Generated by Django 3.2 on 2024-05-15 15:06 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='Rule', + 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()), + ], + ), + ] diff --git a/MisplaceAI/rules/migrations/__init__.py b/MisplaceAI/rules/migrations/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/MisplaceAI/rules/migrations/__pycache__/0001_initial.cpython-310.pyc b/MisplaceAI/rules/migrations/__pycache__/0001_initial.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3cbc07602d64ef4ef2f9a6599d6144f132f80e71 Binary files /dev/null and b/MisplaceAI/rules/migrations/__pycache__/0001_initial.cpython-310.pyc differ diff --git a/MisplaceAI/rules/migrations/__pycache__/__init__.cpython-310.pyc b/MisplaceAI/rules/migrations/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e5d74a725a7fc9f83c4875323be05214b129899b Binary files /dev/null and b/MisplaceAI/rules/migrations/__pycache__/__init__.cpython-310.pyc differ diff --git a/MisplaceAI/rules/models.py b/MisplaceAI/rules/models.py new file mode 100644 index 0000000000000000000000000000000000000000..a11306e1e6a6eb4b42093cc11828aef78a30d7ae --- /dev/null +++ b/MisplaceAI/rules/models.py @@ -0,0 +1,9 @@ +from django.db import models + +class Rule(models.Model): + name = models.CharField(max_length=255, unique=True) + condition = models.TextField() + action = models.TextField() + + def __str__(self): + return self.name diff --git a/MisplaceAI/rules/templates/rules/add_rule.html b/MisplaceAI/rules/templates/rules/add_rule.html new file mode 100644 index 0000000000000000000000000000000000000000..078583c7036c5094e31b133e36381bcbbc924f1f --- /dev/null +++ b/MisplaceAI/rules/templates/rules/add_rule.html @@ -0,0 +1,11 @@ +<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 diff --git a/MisplaceAI/rules/templates/rules/confirm_delete.html b/MisplaceAI/rules/templates/rules/confirm_delete.html new file mode 100644 index 0000000000000000000000000000000000000000..6e2fa104232c0c01d20467a11e83fe68e6a11ad7 --- /dev/null +++ b/MisplaceAI/rules/templates/rules/confirm_delete.html @@ -0,0 +1,18 @@ +<!DOCTYPE html> +<html lang="en"> + +<head> + <meta charset="UTF-8"> + <title>Delete Rule</title> +</head> + +<body> + <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> + </form> +</body> + +</html> \ No newline at end of file diff --git a/MisplaceAI/rules/templates/rules/list_rules.html b/MisplaceAI/rules/templates/rules/list_rules.html new file mode 100644 index 0000000000000000000000000000000000000000..2be2586c9c74fe0b553a56139b4834c11ed49d3e --- /dev/null +++ b/MisplaceAI/rules/templates/rules/list_rules.html @@ -0,0 +1,28 @@ +<!DOCTYPE html> +<html lang="en"> + +<head> + <meta charset="UTF-8"> + <title>List of Rules</title> +</head> + +<body> + <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 diff --git a/MisplaceAI/rules/templates/rules/rule_detail.html b/MisplaceAI/rules/templates/rules/rule_detail.html new file mode 100644 index 0000000000000000000000000000000000000000..5b352318b21d927aa6fbb4ab608a451b2002b65d --- /dev/null +++ b/MisplaceAI/rules/templates/rules/rule_detail.html @@ -0,0 +1,4 @@ +<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 diff --git a/MisplaceAI/rules/templates/rules/update_rule.html b/MisplaceAI/rules/templates/rules/update_rule.html new file mode 100644 index 0000000000000000000000000000000000000000..0a5843eab3c23a2142a42d717c7df01921112130 --- /dev/null +++ b/MisplaceAI/rules/templates/rules/update_rule.html @@ -0,0 +1,6 @@ +<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 diff --git a/MisplaceAI/rules/tests.py b/MisplaceAI/rules/tests.py new file mode 100644 index 0000000000000000000000000000000000000000..7ce503c2dd97ba78597f6ff6e4393132753573f6 --- /dev/null +++ b/MisplaceAI/rules/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/MisplaceAI/rules/urls.py b/MisplaceAI/rules/urls.py new file mode 100644 index 0000000000000000000000000000000000000000..a556df62dcd008059419ee8964c1453ea39695bd --- /dev/null +++ b/MisplaceAI/rules/urls.py @@ -0,0 +1,12 @@ +from django.urls import path +from .views import list_rules, add_rule, get_rule, remove_rule, update_rule + +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'), + +] diff --git a/MisplaceAI/rules/utils.py b/MisplaceAI/rules/utils.py new file mode 100644 index 0000000000000000000000000000000000000000..fd8fbcd06d573a50bfef2137d6d32160a45bb52f --- /dev/null +++ b/MisplaceAI/rules/utils.py @@ -0,0 +1,40 @@ +from .models import Rule + +class RulesManager: + def __init__(self): + self.rules = self.load_rules() + + 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 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()) diff --git a/MisplaceAI/rules/views.py b/MisplaceAI/rules/views.py new file mode 100644 index 0000000000000000000000000000000000000000..9ef587236a10a2358de64098abf0f1b4533776a1 --- /dev/null +++ b/MisplaceAI/rules/views.py @@ -0,0 +1,52 @@ +from django.shortcuts import render, redirect, get_object_or_404 +from django.http import JsonResponse +from .models import Rule +from .forms import RuleForm + +def list_rules(request): + rules = Rule.objects.all() + return render(request, 'rules/list_rules.html', {'rules': rules}) + +def add_rule(request): + if request.method == 'POST': + form = RuleForm(request.POST) + 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}) + +def get_rule(request, rule_name): + rule = get_object_or_404(Rule, name=rule_name) + return render(request, 'rules/rule_detail.html', {'rule': rule}) + +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}) + +def update_rule(request, rule_name): + rule = get_object_or_404(Rule, name=rule_name) + 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}) + else: + form = RuleForm(instance=rule) + return render(request, 'rules/update_rule.html', {'form': form, 'rule': rule}) + + +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})