from django.conf import settings import redis from huey.contrib.djhuey import task, lock_task import operator from django.template.loader import render_to_string 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, progress, result=None): channel_layer = get_channel_layer() async_to_sync(channel_layer.group_send)( group_name, { 'type': 'channel_message', 'message': render_to_string('components/tasks/update.html', { 'progress': progress, 'result': result, }), } ) def render_location_in_the_queue(group_name, location): channel_layer = get_channel_layer() async_to_sync(channel_layer.group_send)( group_name, { 'type': 'channel_message', 'message': render_to_string('components/tasks/location.html', { 'location': location, }), } ) @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, shortest_route) return shortest_route return calculate_shortest_route(distances) @task() @lock_task('run-queue-lock') def run_tasks_from_queue(): notify_of_new_position() # Get the first task from the queue redis_conn = redis.StrictRedis(host=settings.REDIS_HOST, port=settings.REDIS_PORT, db=0) task = redis_conn.lindex('enqueue', 0) if task: # Run task r = calculate_min_distance(task.decode('utf-8')) # Wait for task to finish r(blocking=True) # Run the next task redis_conn.lpop('enqueue') run_tasks_from_queue() @task() def notify_of_new_position(): redis_conn = redis.StrictRedis(host=settings.REDIS_HOST, port=settings.REDIS_PORT, db=0) for index, group_name in enumerate(redis_conn.lrange('enqueue', 0, -1)): render_location_in_the_queue(group_name.decode('utf-8'), index )