You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

475 lines
18 KiB

This file contains invisible Unicode characters!

This file contains invisible Unicode characters that may be processed differently from what appears below. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to reveal hidden characters.

from django.shortcuts import render
from django.http import Http404
import redis
import uuid
from redis_app.utils.index import create_index
# This model only helps us the development part to set rules on objects (we do not use the django model to save objects in it)
from .models import Person
DEFAULT_EXPIRE_TIME = 120
EXPIRE_REFRESH_TIME = 60
COURSE_CHANNEL = "course"
cancel = True
personLoggedIn = ""
redis_connection = redis.Redis(host='localhost', port=6379, decode_responses=True)
pub_sub = redis_connection.pubsub()
create_index(redis_connection=redis_connection)
def isExpired(course_id):
global redis_connection
ttl = redis_connection.ttl(f"course:{course_id}")
if ttl == -2 or ttl == 0: # If -1, the key has no expiration
return True
return False
def getPersonId(person_name, person_role):
query = f"@name:{person_name} @role:{person_role}"
results = redis_connection.execute_command('FT.SEARCH', 'idx:persons', query)
if results[0] > 0:
person_key = results[1]
return person_key
return False
def getCourseId2(course_title, course_teacher):
query = f"@title:{course_title} @teacher:{course_teacher}"
results = redis_connection.execute_command('FT.SEARCH', 'idx:courses', query)
if results[0] > 0:
course_key = results[1]
return course_key
return False
def getCourseId(course):
return getCourseId2(course.get("title"), course.get("teacher"))
def home(request):
global cancel
global personLoggedIn
cancel = True
return render(request, 'home.html', {'person': personLoggedIn})
def isPersonRegisteredToCourse(course):
global personLoggedIn
global redis_connection
if not personLoggedIn:
return False
person_id = getPersonId(personLoggedIn["name"], personLoggedIn["role"]).split(":")[1]
if not person_id:
return False
course_id = getCourseId(course)
if not course_id:
return False
course_keys = redis_connection.keys(f"course:*")
for key in course_keys:
course_data = redis_connection.hgetall(key)
if personLoggedIn['role'] == "Student":
if person_id in course_data.get("students", "").split(","):
current_course_id = getCourseId2(course_data['title'], course_data['teacher'])
if current_course_id == False:
continue
if course_id == current_course_id:
return True
return False
def details(request, course_id):
global redis_connection
global cancel
global personLoggedIn
cancel = True
if isExpired(course_id):
raise Http404("Course has expired... Get faster next time!")
course = redis_connection.hgetall(f"course:{course_id}")
if not course:
raise Http404("Course does not exist")
course['id'] = course_id
teacher = redis_connection.hgetall(f"person:{course['teacher']}")
if not teacher:
raise Http404("Teacher does not exist")
course['teacher_name'] = teacher.get('name')
register = isPersonRegisteredToCourse(course)
return render(request, 'details.html', {'course': course, 'person': personLoggedIn, 'register': register})
# Handle error messages.
def register(request):
global personLoggedIn
global redis_connection
global cancel
cancel = True
if request.method == 'POST':
person_name = request.POST.get('name')
person_role = request.POST.get('role')
query = f"@name:{person_name} @role:{person_role}"
results = redis_connection.execute_command('FT.SEARCH', 'idx:persons', query)
# First element = number of results
if results[0] > 0:
return render(request, 'register.html', {'person': Person(name="", role=""), "error_message": "This user already exists!"})
person_key = f"person:{uuid.uuid4()}"
redis_connection.hset(person_key, mapping={
'name': person_name,
'role': person_role
})
personLoggedIn = redis_connection.hgetall(person_key)
return home(request)
return render(request, 'register.html', {'person': Person(name="", role=""), "error_message": "The form has been wrongly filled!"})
def login(request):
global personLoggedIn
global redis_connection
global cancel
cancel = True
if request.method == 'POST':
person_name = request.POST.get('name')
person_role = request.POST.get('role')
query = f"@name:{person_name} @role:{person_role}"
results = redis_connection.execute_command('FT.SEARCH', 'idx:persons', query)
if results[0] > 0:
person_key = results[1]
personLoggedIn = redis_connection.hgetall(person_key)
return home(request)
return render(request, 'login.html', {'person': Person(name="", role=""), "error_message": "No user found!"})
return login_form(request)
def register_form(request):
global cancel
cancel = True
return render(request, 'register.html', {'person': Person(name="", role="")})
def login_form(request):
global cancel
cancel = True
return render(request, 'login.html', {'person': Person(name="", role="")})
def logout(request):
global personLoggedIn
global cancel
cancel = True
personLoggedIn = ""
return render(request, 'login.html', {'person': Person(name="", role="")})
def courses(request):
global personLoggedIn
global cancel
cancel = True
if personLoggedIn == "":
return render(request, 'login.html', {'person': Person(name="", role=""), "error_message": "You must login!"})
person_id = getPersonId(personLoggedIn["name"], personLoggedIn["role"]).split(":")[1]
course_keys = redis_connection.keys(f"course:*")
courses = []
expired_courses = []
for key in course_keys:
course_data = redis_connection.hgetall(key)
if personLoggedIn['role'] == "Student":
if person_id in course_data.get("students", "").split(","):
course_id = getCourseId2(course_data['title'], course_data['teacher'])
if course_id == False:
continue
course_data['id'] = course_id.split(":")[1]
if isExpired(course_data['id']):
expired_courses.append(course_data)
else:
courses.append(course_data)
else:
teacher_id = course_data["teacher"]
if teacher_id == person_id:
course_id = getCourseId2(course_data['title'], course_data['teacher'])
if course_id == False:
continue
course_data['id'] = course_id.split(":")[1]
if isExpired(course_data['id']):
expired_courses.append(course_data)
else:
courses.append(course_data)
return render(request, 'courses.html', {'person': personLoggedIn, 'courses': courses, 'expired_courses': expired_courses})
def change_profile(request):
global personLoggedIn
global cancel
cancel = True
if personLoggedIn == '':
return render(request, 'login.html', {'person': Person(name="", role=""), "error_message": "You must login!"})
if request.method == 'POST':
person_name = request.POST.get('name')
person_role = request.POST.get('role')
person_id = getPersonId(personLoggedIn["name"], personLoggedIn["role"])
if not person_id:
return render(request, 'profile.html', {'person': personLoggedIn, "error_message": "Internal error: No user found!"})
redis_connection.hmset(person_id, mapping={"name": person_name, "role": person_role})
personLoggedIn["name"] = person_name
personLoggedIn["role"] = person_role
return render(request, 'profile.html', {'person': personLoggedIn})
return login_form(request)
def profile(request):
global personLoggedIn
global cancel
cancel = True
if personLoggedIn == '':
return render(request, 'login.html', {'person': Person(name="", role=""), "error_message": "You must login!"})
return render(request, 'profile.html', {'person': personLoggedIn})
def delete_course(course_id):
course = redis_connection.hgetall(f"course:{course_id}")
if not course:
return False
redis_connection.delete(f"course:{course_id}")
return True
def delete_course_view(request, course_id):
global personLoggedIn
res = delete_course(course_id)
if isExpired(course_id):
raise Http404("Course has expired... Get faster next time!")
if not res:
return courses(request)
return courses(request)
def create_course(course_title, course_summary, course_level, course_places, course_teacher):
course_id = uuid.uuid4()
redis_connection.hset(f"course:{course_id}", mapping={
"title": course_title,
"summary": course_summary,
"level": course_level,
"places": course_places,
"teacher": course_teacher
})
redis_connection.expire(f"course:{course_id}", DEFAULT_EXPIRE_TIME)
def update_course(course_id, course_title, course_summary, course_level, course_places, course_teacher):
redis_connection.hset(f"course:{course_id}", mapping={
"title": course_title,
"summary": course_summary,
"level": course_level,
"places": course_places,
"teacher": course_teacher
})
redis_connection.expire(f"course:{course_id}", DEFAULT_EXPIRE_TIME)
def create_course_view(request):
global personLoggedIn
return render(request, 'create.html', {'person': personLoggedIn})
def update_course_view(request, course_id):
global personLoggedIn
if isExpired(course_id):
raise Http404("Course has expired... Get faster next time!")
course = redis_connection.hgetall(f"course:{course_id}")
if not course:
raise Http404("Course does not exist")
course_id = getCourseId2(course['title'], course['teacher'])
if course_id == False:
raise Http404("Course does not exist")
course['id'] = course_id.split(":")[1]
return render(request, 'update.html', {'person': personLoggedIn, 'course': course})
def create_course_form(request):
global personLoggedIn
global redis_connection
global cancel
cancel = True
if personLoggedIn == '':
return render(request, 'login.html', {'person': Person(name="", role=""), "error_message": "You must login!"})
if request.method == 'POST':
course_title = request.POST.get('title')
course_summary = request.POST.get('summary')
course_level = request.POST.get('level')
course_places = request.POST.get('places')
course_teacher = getPersonId(personLoggedIn.get('name'), personLoggedIn.get('role')).split(":")[1]
create_course(course_title, course_summary, course_level, course_places, course_teacher)
return courses(request)
return render(request, 'create.html', {'person': personLoggedIn, "error_message": "The form has been wrongly filled!"})
def update_course_form(request, course_id):
global personLoggedIn
global redis_connection
global cancel
cancel = True
if isExpired(course_id):
raise Http404("Course has expired... Get faster next time!")
if personLoggedIn == '':
return render(request, 'login.html', {'person': Person(name="", role=""), "error_message": "You must login!"})
if request.method == 'POST':
course_title = request.POST.get('title')
course_summary = request.POST.get('summary')
course_level = request.POST.get('level')
course_places = request.POST.get('places')
course_teacher = getPersonId(personLoggedIn.get('name'), personLoggedIn.get('role')).split(":")[1]
update_course(course_id, course_title, course_summary, course_level, course_places, course_teacher)
return courses(request)
return render(request, 'update.html', {'person': personLoggedIn, "error_message": "The form has been wrongly filled!"})
# Test if places are not limited
def course_register(course_id, person_id):
course = redis_connection.hgetall(f"course:{course_id}")
person = redis_connection.hgetall(f"person:{person_id}")
if not course or not person:
return False
students = course.get('students', '')
if person_id in students.split(','):
return True
if not students:
new_students = person_id
else:
new_students = students + "," + person_id
redis_connection.hset(f"course:{course_id}", "students", new_students)
return True
def course_unregister(course_id, person_id):
course = redis_connection.hgetall(f"course:{course_id}")
person = redis_connection.hgetall(f"person:{person_id}")
if not course or not person:
return False
students = course.get('students', '')
if person_id not in students.split(','):
return True
students_list = students.split(",")
if len(students_list) == 1:
new_students = ""
else:
new_students = ",".join([student for student in students_list if student != person_id])
redis_connection.hset(f"course:{course_id}", "students", new_students)
return True
def course_register_view(request, course_id):
global personLoggedIn
global redis_connection
if isExpired(course_id):
raise Http404("Course has expired... Get faster next time!")
person_id = getPersonId(personLoggedIn["name"], personLoggedIn["role"]).split(":")[1]
if not person_id:
raise Http404("Person not found")
course = redis_connection.hgetall(f"course:{course_id}")
if not course:
raise Http404("Course not found")
course["id"] = course_id
teacher = redis_connection.hgetall(f"person:{course['teacher']}")
if not teacher:
raise Http404("Teacher does not exist")
course['teacher_name'] = teacher.get('name')
if course_register(course_id, person_id):
return render(request, 'details.html', {'person': personLoggedIn, 'course': course, 'register': True})
return render(request, 'details.html', {'person': personLoggedIn, 'course': course, 'register': False, "error_message": "Could not register to the course. Try again later."})
def course_unregister_view(request, course_id):
global personLoggedIn
global redis_connection
if personLoggedIn == "":
return render(request, 'login.html', {'person': Person(name="", role=""), "error_message": "You must login!"})
if isExpired(course_id):
raise Http404("Course has expired... Get faster next time!")
person_id = getPersonId(personLoggedIn["name"], personLoggedIn["role"]).split(":")[1]
if not person_id:
raise Http404("Person not found")
course = redis_connection.hgetall(f"course:{course_id}")
if not course:
raise Http404("Course not found")
course["id"] = course_id
teacher = redis_connection.hgetall(f"person:{course['teacher']}")
if not teacher:
raise Http404("Teacher does not exist")
course['teacher_name'] = teacher.get('name')
if course_unregister(course_id, person_id):
return render(request, 'details.html', {'person': personLoggedIn, 'course': course, 'register': False})
return render(request, 'details.html', {'person': personLoggedIn, 'course': course, 'register': False, "error_message": "Could not unregister from the course. Try again later."})
def refresh_expire(course_id):
course = redis_connection.hgetall(f"course:{course_id}")
if not course:
return False
ttl = redis_connection.ttl(f"course:{course_id}")
if ttl <= 0:
return False
redis_connection.expire(f"course:{course_id}", ttl + EXPIRE_REFRESH_TIME)
return True
def publish(course):
pub_sub.publish(COURSE_CHANNEL + ":" + getCourseId(course), str(course))
# Courses is a list of coursee id to subscribe to
def subscribe(courses_id):
global cancel
cancel = False
pub_sub.psubscribe(*courses_id)
for message in pub_sub.listen():
if cancel:
break
if message["type"] != 'pmessage':
continue
send_new_course_notification(message)
def send_new_course_notification(message):
print(message)
def search(keywords):
courses = []
for keyword in keywords.split(' '):
if not keyword: # When the users end his keywords with a ' '
continue
title_query = f"@title:{keyword}"
summary_query = f"@summary:{keyword}"
level_query = f"@level:{keyword}"
teacher_query = f"@teacher:{keyword}"
title_results = redis_connection.execute_command('FT.SEARCH', 'idx:courses', title_query)
summary_results = redis_connection.execute_command('FT.SEARCH', 'idx:courses', summary_query)
level_results = redis_connection.execute_command('FT.SEARCH', 'idx:courses', level_query)
teacher_results = redis_connection.execute_command('FT.SEARCH', 'idx:courses', teacher_query)
for i in range(1, len(title_results), 2):
courses.append(title_results[i])
for i in range(1, len(summary_results), 2):
courses.append(summary_results[i])
for i in range(1, len(level_results), 2):
courses.append(level_results[i])
for i in range(1, len(teacher_results), 2):
courses.append(teacher_results[i])
return courses
def search_view(request):
global personLoggedIn
return render(request, 'search.html', {'person': personLoggedIn})
def search_form(request):
global personLoggedIn
global redis_connection
global cancel
cancel = True
if request.method == 'POST':
keywords = request.POST.get('keywords')
courses = []
courses_id = search(keywords)
for course_id in courses_id:
course = redis_connection.hgetall(course_id)
if not course:
continue
course['id'] = course_id.split(":")[1]
courses.append(course)
return render(request, 'search.html', {'person': personLoggedIn, 'courses': courses})
return render(request, 'search.html', {'person': personLoggedIn, "error_message": "The form has been wrongly filled!"})
# Every id is only the number, not the course:number or person:number