Add files
This commit is contained in:
		| @@ -26,4 +26,5 @@ RUN pip3 install -r requirements.txt | ||||
|  | ||||
| EXPOSE 8000 | ||||
|  | ||||
| ENTRYPOINT ["python3", "manage.py", "runserver", "0.0.0.0:8000"] | ||||
| CMD ["python3", "manage.py", "runserver", "0.0.0.0:8000"] | ||||
| #CMD ["daphne", "-b", "0.0.0.0", "-p", "8000", "myproject.asgi:application"] | ||||
|   | ||||
| @@ -19,15 +19,14 @@ services: | ||||
|       - .:/usr/src/app/ | ||||
|     ports: | ||||
|       - 8000:8000 | ||||
|     depends_on: | ||||
|       - redis | ||||
|  | ||||
|   huey: | ||||
|     build: . | ||||
|     restart: "no" | ||||
|     entrypoint: huey_consumer.py app.huey -w 4 -k process -f | ||||
|     entrypoint: ./manage.py run_huey -f | ||||
|     volumes: | ||||
|       - .:/usr/src/app | ||||
|     environment: | ||||
|       - STORE_URI=redis://redis:6379/0 | ||||
|     depends_on: | ||||
|       - redis | ||||
|   | ||||
							
								
								
									
										22
									
								
								manage.py
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										22
									
								
								manage.py
									
									
									
									
									
										Executable file
									
								
							| @@ -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', 'myproject.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() | ||||
							
								
								
									
										0
									
								
								myproject/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								myproject/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										31
									
								
								myproject/asgi.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								myproject/asgi.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,31 @@ | ||||
| """ | ||||
| ASGI config for myproject 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/5.1/howto/deployment/asgi/ | ||||
| """ | ||||
| import os | ||||
|  | ||||
| os.environ.setdefault("DJANGO_SETTINGS_MODULE", "settings") | ||||
| import django | ||||
| django.setup() | ||||
|  | ||||
| from django.core.asgi import get_asgi_application | ||||
| from channels.security.websocket import AllowedHostsOriginValidator | ||||
| from channels.routing import ProtocolTypeRouter, URLRouter | ||||
| from django.urls import re_path | ||||
| from waiting_room.consumers import MyConsumer | ||||
|  | ||||
|  | ||||
| application = ProtocolTypeRouter( | ||||
|     { | ||||
|         # Django's ASGI application to handle traditional HTTP requests | ||||
|         "http": get_asgi_application(), | ||||
|         # WebSocket handler | ||||
|         "websocket": AllowedHostsOriginValidator( | ||||
|                 URLRouter([re_path(r"^ws/(?P<room_name>[a-zA-Z0-9_]+)/$", MyConsumer.as_asgi())]) | ||||
|         ), | ||||
|     } | ||||
| ) | ||||
							
								
								
									
										165
									
								
								myproject/settings.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										165
									
								
								myproject/settings.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,165 @@ | ||||
| """ | ||||
| Django settings for myproject project. | ||||
|  | ||||
| Generated by 'django-admin startproject' using Django 5.1.2. | ||||
|  | ||||
| For more information on this file, see | ||||
| https://docs.djangoproject.com/en/5.1/topics/settings/ | ||||
|  | ||||
| For the full list of settings and their values, see | ||||
| https://docs.djangoproject.com/en/5.1/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/5.1/howto/deployment/checklist/ | ||||
|  | ||||
| # SECURITY WARNING: keep the secret key used in production secret! | ||||
| SECRET_KEY = 'django-insecure-ut&)d&0sv4tb17^y1tt$er8m@!9lke7*qs(9&m$mdj8297qmnh' | ||||
|  | ||||
| # SECURITY WARNING: don't run with debug turned on in production! | ||||
| DEBUG = True | ||||
|  | ||||
| ALLOWED_HOSTS = ["*"] | ||||
|  | ||||
|  | ||||
| # Application definition | ||||
|  | ||||
| INSTALLED_APPS = [ | ||||
|     'daphne', | ||||
|     'django.contrib.admin', | ||||
|     'django.contrib.auth', | ||||
|     'django.contrib.contenttypes', | ||||
|     'django.contrib.sessions', | ||||
|     'django.contrib.messages', | ||||
|     'django.contrib.staticfiles', | ||||
|     'waiting_room', # Nuestra aplicación | ||||
|     'huey.contrib.djhuey', # Colas de tareas | ||||
|     'channels', # Servidor de WebSockets | ||||
| ] | ||||
|  | ||||
| CHANNEL_LAYERS = { | ||||
|     "default": { | ||||
|         "BACKEND": "channels_redis.core.RedisChannelLayer", | ||||
|         "CONFIG": { | ||||
|             "hosts": [("redis", 6379)], | ||||
|         }, | ||||
|     }, | ||||
| } | ||||
|  | ||||
| HUEY = { | ||||
|     'huey_class': 'huey.RedisHuey', | ||||
|     'name': 'queue', | ||||
|     'results': True, | ||||
|     'store_none': False, | ||||
|     'immediate': False, | ||||
|     'utc': False, | ||||
|     'blocking': True, | ||||
|     'connection': { | ||||
|         'host': 'redis', | ||||
|         'port': 6379, | ||||
|         'db': 0, | ||||
|         'connection_pool': None, | ||||
|         'read_timeout': 1, | ||||
|         'url': None, | ||||
|     }, | ||||
|     'consumer': { | ||||
|         'workers': 4, | ||||
|         'worker_type': 'thread', | ||||
|         'initial_delay': 0.1, | ||||
|         'backoff': 1.15, | ||||
|         'max_delay': 10.0, | ||||
|         'scheduler_interval': 1, | ||||
|         'periodic': True, | ||||
|         'check_worker_health': True, | ||||
|         'health_check_interval': 1, | ||||
|     }, | ||||
| } | ||||
|  | ||||
| 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 = 'myproject.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', | ||||
|             ], | ||||
|         }, | ||||
|     }, | ||||
| ] | ||||
|  | ||||
| ASGI_APPLICATION = 'myproject.asgi.application' | ||||
|  | ||||
|  | ||||
| # Database | ||||
| # https://docs.djangoproject.com/en/5.1/ref/settings/#databases | ||||
|  | ||||
| DATABASES = { | ||||
|     'default': { | ||||
|         'ENGINE': 'django.db.backends.sqlite3', | ||||
|         'NAME': BASE_DIR / 'db.sqlite3', | ||||
|     } | ||||
| } | ||||
|  | ||||
|  | ||||
| # Password validation | ||||
| # https://docs.djangoproject.com/en/5.1/ref/settings/#auth-password-validators | ||||
|  | ||||
| AUTH_PASSWORD_VALIDATORS = [ | ||||
|     { | ||||
|         'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', | ||||
|     }, | ||||
|     { | ||||
|         'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', | ||||
|     }, | ||||
|     { | ||||
|         'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', | ||||
|     }, | ||||
|     { | ||||
|         'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', | ||||
|     }, | ||||
| ] | ||||
|  | ||||
|  | ||||
| # Internationalization | ||||
| # https://docs.djangoproject.com/en/5.1/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/5.1/howto/static-files/ | ||||
|  | ||||
| STATIC_URL = 'static/' | ||||
|  | ||||
| # Default primary key field type | ||||
| # https://docs.djangoproject.com/en/5.1/ref/settings/#default-auto-field | ||||
|  | ||||
| DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' | ||||
							
								
								
									
										22
									
								
								myproject/urls.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								myproject/urls.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,22 @@ | ||||
| """ | ||||
| URL configuration for myproject project. | ||||
|  | ||||
| The `urlpatterns` list routes URLs to views. For more information please see: | ||||
|     https://docs.djangoproject.com/en/5.1/topics/http/urls/ | ||||
| Examples: | ||||
| Function views | ||||
|     1. Add an import:  from my_app import views | ||||
|     2. Add a URL to urlpatterns:  path('', views.home, name='home') | ||||
| Class-based views | ||||
|     1. Add an import:  from other_app.views import Home | ||||
|     2. Add a URL to urlpatterns:  path('', Home.as_view(), name='home') | ||||
| Including another URLconf | ||||
|     1. Import the include() function: from django.urls import include, path | ||||
|     2. Add a URL to urlpatterns:  path('blog/', include('blog.urls')) | ||||
| """ | ||||
| from django.contrib import admin | ||||
| from django.urls import path | ||||
|  | ||||
| urlpatterns = [ | ||||
|     path('admin/', admin.site.urls), | ||||
| ] | ||||
| @@ -5,8 +5,8 @@ asgiref==3.8.1 | ||||
|  | ||||
| # Django Channels | ||||
| channels==4.1.0 | ||||
| channels_redis==4.2.0 | ||||
| redis==5.2.0 | ||||
| asgi_redis==1.4.3 | ||||
|  | ||||
| # Task queue | ||||
| huey==2.5.2 | ||||
|   | ||||
							
								
								
									
										0
									
								
								waiting_room/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								waiting_room/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										3
									
								
								waiting_room/admin.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								waiting_room/admin.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,3 @@ | ||||
| from django.contrib import admin | ||||
|  | ||||
| # Register your models here. | ||||
							
								
								
									
										6
									
								
								waiting_room/apps.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								waiting_room/apps.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,6 @@ | ||||
| from django.apps import AppConfig | ||||
|  | ||||
|  | ||||
| class WaitingRoomConfig(AppConfig): | ||||
|     default_auto_field = 'django.db.models.BigAutoField' | ||||
|     name = 'waiting_room' | ||||
							
								
								
									
										26
									
								
								waiting_room/consumers.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								waiting_room/consumers.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,26 @@ | ||||
| import json | ||||
| from channels.generic.websocket import WebsocketConsumer | ||||
| from asgiref.sync import async_to_sync | ||||
| from waiting_room.tasks import calculate_min_distance | ||||
|  | ||||
| class MyConsumer(WebsocketConsumer): | ||||
|  | ||||
|     def connect(self): | ||||
|         self.room_group_name = self.scope["url_route"]["kwargs"]["room_name"] | ||||
|         async_to_sync(self.channel_layer.group_add)(self.room_group_name, self.channel_name) | ||||
|         self.accept() | ||||
|         calculate_min_distance(self.room_group_name) | ||||
|  | ||||
|     def disconnect(self, close_code): | ||||
|         async_to_sync(self.channel_layer.group_discard)(self.room_group_name, self.channel_name) | ||||
|         self.close() | ||||
|  | ||||
|     def receive(self, text_data): | ||||
|         # Echo | ||||
|         self.send(text_data=text_data) | ||||
|  | ||||
|     def channel_message(self, event): | ||||
|         message = event['message'] | ||||
|  | ||||
|         # Send message to WebSocket | ||||
|         self.send(text_data=str(message)) | ||||
							
								
								
									
										0
									
								
								waiting_room/migrations/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								waiting_room/migrations/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										3
									
								
								waiting_room/models.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								waiting_room/models.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,3 @@ | ||||
| from django.db import models | ||||
|  | ||||
| # Create your models here. | ||||
							
								
								
									
										70
									
								
								waiting_room/tasks.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										70
									
								
								waiting_room/tasks.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,70 @@ | ||||
| from huey.contrib.djhuey import task | ||||
| import operator | ||||
| from itertools import permutations | ||||
| from collections import Counter | ||||
| from functools import reduce | ||||
| from math import factorial | ||||
| from channels.layers import get_channel_layer | ||||
| from asgiref.sync import async_to_sync | ||||
|  | ||||
| def render_progress_bar(group_name, message): | ||||
|     channel_layer = get_channel_layer() | ||||
|     async_to_sync(channel_layer.group_send)( | ||||
|         group_name, | ||||
|         { | ||||
|             'type': 'channel_message', | ||||
|             'message': message | ||||
|         } | ||||
|     ) | ||||
|  | ||||
| @task() | ||||
| def calculate_min_distance(group_name): | ||||
|     # Distance matrix between cities | ||||
|     distances = [ | ||||
|         [0, 29, 20, 21, 16, 31, 100, 12, 5, 78], | ||||
|         [29, 0, 15, 29, 28, 40, 72, 21, 29, 41], | ||||
|         [20, 15, 0, 15, 14, 25, 81, 9, 23, 27], | ||||
|         [21, 29, 15, 0, 4, 12, 92, 12, 25, 13], | ||||
|         [16, 28, 14, 4, 0, 16, 94, 9, 20, 16], | ||||
|         [31, 40, 25, 12, 16, 0, 95, 24, 36, 3], | ||||
|         [100, 72, 81, 92, 94, 95, 0, 90, 101, 99], | ||||
|         [12, 21, 9, 12, 9, 24, 90, 0, 15, 25], | ||||
|         [5, 29, 23, 25, 20, 36, 101, 15, 0, 35], | ||||
|         [78, 41, 27, 13, 16, 3, 99, 25, 35, 0], | ||||
|     ] | ||||
|  | ||||
|     num_cities = len(distances) | ||||
|     cities = list(range(num_cities)) | ||||
|  | ||||
|     def count_permutations(sequence): | ||||
|         total = factorial(len(sequence)) | ||||
|         duplicates = Counter(sequence).values() | ||||
|         divisor = reduce(operator.mul, (factorial(v) for v in duplicates), 1) | ||||
|         return total / divisor | ||||
|  | ||||
|     def calculate_shortest_route(distances): | ||||
|         shortest_route = float("inf") | ||||
|         city_count = 0 | ||||
|  | ||||
|         # Calculate all possible permutations of cities (routes) | ||||
|         total_permutations = count_permutations(cities) | ||||
|  | ||||
|         percentaje = 0 | ||||
|         for perm in permutations(cities): | ||||
|             # Send progress to the group | ||||
|             temp_percentaje = int(city_count / total_permutations * 100) | ||||
|             if temp_percentaje != percentaje: | ||||
|                 percentaje = temp_percentaje | ||||
|                 render_progress_bar(group_name, percentaje) | ||||
|             city_count += 1 | ||||
|             # Calculate the distance of the route | ||||
|             route_distance = 0 | ||||
|             for i in range(num_cities - 1): | ||||
|                 route_distance += distances[perm[i]][perm[i + 1]] | ||||
|             route_distance += distances[perm[-1]][perm[0]]  # Back to the start city | ||||
|             shortest_route = min(shortest_route, route_distance) | ||||
|  | ||||
|         render_progress_bar(group_name, 100) | ||||
|         return shortest_route | ||||
|  | ||||
|     return calculate_shortest_route(distances) | ||||
							
								
								
									
										3
									
								
								waiting_room/tests.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								waiting_room/tests.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,3 @@ | ||||
| from django.test import TestCase | ||||
|  | ||||
| # Create your tests here. | ||||
							
								
								
									
										3
									
								
								waiting_room/views.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								waiting_room/views.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,3 @@ | ||||
| from django.shortcuts import render | ||||
|  | ||||
| # Create your views here. | ||||
		Reference in New Issue
	
	Block a user