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.

528 lines
21 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 = 1200
EXPIRE_REFRESH_TIME = 60
messages = [] # All messages obtained with pub/sub. Will refresh the view every time a message is received
redis_connection = redis.Redis(host='localhost', port=6379, decode_responses=True)
pub_sub = redis_connection.pubsub()
create_index(redis_connection=redis_connection)
def get_uuid4():
return str(uuid.uuid4()).replace('-', '')
def getCoursesFromPerson(person_id):
global redis_connection
course_keys = redis_connection.keys(f"course:*")
courses = []
person = redis_connection.hgetall(f"person:{person_id}")
for key in course_keys:
course_data = redis_connection.hgetall(key)
if person['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]
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]
courses.append(course_data)
return courses
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 isCourseFull(course):
students = course.get('students', '') # Because it crashes with ['students'], we have to put a default value: '' here
if not students:
return False
try:
places = int(course['places'])
if len(students.split(',')) >= places:
return True
return False
except:
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['title'], course['teacher'])
def home(request):
person = request.session.get("person", "")
return render(request, 'home.html', {'person': person})
def isPersonRegisteredToCourse(course, person):
global redis_connection
if not person:
return False
person_id = getPersonId(person["name"], person["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 person['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, error_message=''):
person = request.session.get("person", "")
global redis_connection
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['name']
register = isPersonRegisteredToCourse(course, person)
full = isCourseFull(course)
return render(request, 'details.html', {'course': course, 'person': person, 'register': register, 'full': full, 'error_message': error_message})
def register(request):
if request.method == 'POST':
person_name = request.POST['name']
person_role = request.POST['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:{get_uuid4()}"
redis_connection.hset(person_key, mapping={
'name': person_name,
'role': person_role
})
request.session["person"] = 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):
if request.method == 'POST':
person_name = request.POST['name']
person_role = request.POST['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]
request.session["person"] = 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):
return render(request, 'register.html', {'person': Person(name="", role="")})
def login_form(request):
return render(request, 'login.html', {'person': Person(name="", role="")})
def logout(request):
request.session["person"] = ""
return render(request, 'login.html', {'person': Person(name="", role="")})
def courses(request):
person = request.session.get("person", "")
if person == "":
return render(request, 'login.html', {'person': Person(name="", role=""), "error_message": "You must login!"})
person_id = getPersonId(person["name"], person["role"]).split(":")[1]
courses = getCoursesFromPerson(person_id)
return render(request, 'courses.html', {'person': person, 'courses': courses})
def change_profile(request):
person = request.session.get("person", "")
if person == '':
return render(request, 'login.html', {'person': Person(name="", role=""), "error_message": "You must login!"})
if request.method == 'POST':
person_name = request.POST['name']
person_role = request.POST['role']
person_id = getPersonId(person["name"], person["role"])
if not person_id:
return render(request, 'profile.html', {'person': person, "error_message": "Internal error: No user found!"})
redis_connection.hmset(person_id, mapping={"name": person_name, "role": person_role})
person["name"] = person_name
person["role"] = person_role
request.session["person"] = person
return render(request, 'profile.html', {'person': person})
return login_form(request)
def profile(request):
person = request.session.get("person", "")
if person == '':
return render(request, 'login.html', {'person': Person(name="", role=""), "error_message": "You must login!"})
return render(request, 'profile.html', {'person': person})
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):
if isExpired(course_id):
raise Http404("Course has expired... Get faster next time!")
res = delete_course(course_id)
if not res:
return courses(request)
publish(course_id, f"DELETE: Course:{course_id} has been deleted!")
return courses(request)
def create_course(course_title, course_summary, course_level, course_places, course_teacher):
course_id = get_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)
publish(course_id, f"UPDATE: Course:{course_id} is now: Title: {course_title}, Summary: {course_summary}, Level: {course_level}, Places: {course_places}, Teacher: {course_teacher}")
def publish_message(request):
person = request.session.get("person", "")
courses_fetched = getCoursesFromPerson(getPersonId(person['name'], person['role']).split(":")[1])
for course in courses_fetched:
course['id'] = getCourseId(course).split(':')[1]
if not courses_fetched:
return courses(request)
return render(request, 'publish_message.html', {'person': person, 'courses': courses_fetched})
def publish_message_form(request):
person = request.session.get("person", "")
global redis_connection
if person == '':
return render(request, 'login.html', {'person': Person(name="", role=""), "error_message": "You must login!"})
if person['role'] == 'Student':
return render(request, 'publish_message.html', {'person': person, "error_message": "The form has been wrongly filled!"})
if request.method == 'POST':
course_id = request.POST['course_id']
message = request.POST['message']
course = redis_connection.hgetall(f'course:{course_id}')
if not course:
raise Http404("Course not found")
publish(course_id, message)
return publish_message(request)
return render(request, 'publish_message.html', {'person': person, "error_message": "The form has been wrongly filled!"})
def create_course_view(request):
person = request.session.get("person", "")
return render(request, 'create.html', {'person': person})
def update_course_view(request, course_id):
person = request.session.get("person", "")
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': person, 'course': course})
def create_course_form(request):
person = request.session.get("person", "")
global redis_connection
if person == '':
return render(request, 'login.html', {'person': Person(name="", role=""), "error_message": "You must login!"})
if request.method == 'POST':
course_title = request.POST['title']
course_summary = request.POST['summary']
course_level = request.POST['level']
course_places = request.POST['places']
course_teacher = getPersonId(person['name'], person['role']).split(":")[1]
create_course(course_title, course_summary, course_level, course_places, course_teacher)
return courses(request)
return render(request, 'create.html', {'person': person, "error_message": "The form has been wrongly filled!"})
def update_course_form(request, course_id):
person = request.session.get("person", "")
global redis_connection
if isExpired(course_id):
raise Http404("Course has expired... Get faster next time!")
if person == '':
return render(request, 'login.html', {'person': Person(name="", role=""), "error_message": "You must login!"})
if request.method == 'POST':
course_title = request.POST['title']
course_summary = request.POST['summary']
course_level = request.POST['level']
course_places = request.POST['places']
course_teacher = getPersonId(person['name'], person['role']).split(":")[1]
course = redis_connection.hgetall(f"course:{course_id}")
course["id"] = course_id
course_students = course.get("students", "")
try:
if course_students and len(course_students.split(',')) >= int(course_places):
return render(request, 'update.html', {'person': person, "error_message": "There's too many students registered to decrease the number of places that much!", "course": course})
except:
return render(request, 'update.html', {'person': person, "error_message": "The number of places has not the right format!", "course": course})
update_course(course_id, course_title, course_summary, course_level, course_places, course_teacher)
return courses(request)
return render(request, 'update.html', {'person': person, "error_message": "The form has been wrongly filled!", "course": course})
# 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
try:
places = int(course['places'])
if students != '':
students_nb = len(students.split(','))
if students_nb and students_nb >= places:
return False
except:
return False
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):
person = request.session.get("person", "")
global redis_connection
if isExpired(course_id):
raise Http404("Course has expired... Get faster next time!")
person_id = getPersonId(person["name"], person["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['name']
if course_register(course_id, person_id):
return details(request, course_id)
return details(request, course_id, "Could not register to the course. Try again later.")
def course_unregister_view(request, course_id):
person = request.session.get("person", "")
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(person["name"], person["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['name']
if course_unregister(course_id, person_id):
return details(request, course_id)
return details(request, course_id, "Could not unregister to 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_id, message):
global redis_connection
redis_connection.publish(course_id, message)
# Courses is a list of course id to subscribe to
def subscribe(courses_id):
pub_sub.psubscribe(*courses_id)
for message in pub_sub.listen():
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):
person = request.session.get("person", "")
return render(request, 'search.html', {'person': person})
def search_form(request):
person = request.session.get("person", "")
global redis_connection
if request.method == 'POST':
keywords = request.POST['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': person, 'courses': courses})
return render(request, 'search.html', {'person': person, "error_message": "The form has been wrongly filled!"})
def notifications_view(request):
person = request.session.get("person", "")
global messages
return render(request, 'notifications.html', {'person': person, 'messages': messages})
def notifications(request):
person = request.session.get("person", "")
global messages
if person == "":
return render(request, 'login.html', {'person': Person(name="", role=""), "error_message": "You must login!"})
pub_sub.punsubscribe()
courses_id = [course['id'] for course in getCoursesFromPerson(getPersonId(person['name'], person['role']).split(":")[1])]
if not courses_id:
return courses(request)
pub_sub.psubscribe(*courses_id)
for message in pub_sub.listen():
if message["type"] != 'pmessage':
continue
messages.append(message)
return notifications_view(request)
def clear_notifications(request):
global messages
messages = []
return notifications_view(request)
# Every id is only the number, not the course:number or person:number