112 lines
3.8 KiB
Python
112 lines
3.8 KiB
Python
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 )
|