@ -108,4 +108,23 @@ If you see any bugs, please tell me. I really tried my best by testing every fea
## Django model
I'm using django model a little in my code only to create me some "barriers". this standardize my objects from redis to avoid lot's of bugs. This is only kinda sort of structure to format my data.
I'm using django model a little in my code only to create me some "barriers". this standardize my objects from redis to avoid lot's of bugs. This is only kinda sort of structure to format my data.
##Pub/Sub
To implement the Redis pub/sub in Django, I decided to use this mechanism:
- The `notifications_view` is firstly displayed
- This view calls the `notifications` function thanks to HTML and JS
- This calls the get_message function, waiting for a new message
- While waiting this message, the HTMl refreshs the page every 10s to avoid the timeout
- When a message is received, `notifications` function calls back `notifications_view` and this repeats.
It stops when the user change page. So, the page is constantly "loading" waiting for a message. This allowed me to get real-time message. It's not the most efficient
way to do this but I really didn't want to use websockets or JS libraries to do this. It seems the fastest easiest way to do it.
Also, I saw in some docs we could perform some async functions in Django, but seems kinda long and heavy to implement, so I choose only this method.
# Getting all of courses keys (not into person object because even if we use redis, which is a NoSQL technology, I really wanted to
# stay the most consistent I could by not having two lists for the same data. For example, the courses has a students list. If the students would have
# a courses list, the data would have been duplicated, for no reason except the performance. I decided not to take the risk to duplicate this data
# a courses list, the data would have been duplicated, for no reason except the efficience. I decided not to take the risk to duplicate this data
# because I strongly believe that it will stay optimized enough to iterate over all courses. Also, I decided to put the list for the courses because
# generally there will be more students than courses (because for one course, there's around 30 students), so iterate over the persons (students) would have
# been less performant than over courses. This is only a choiceof implementation. I could obviously be wrong. But that's my choice, and of course this choice
# been less efficient than over courses. This is only a choiceof implementation. I could obviously be wrong. But that's my choice, and of course this choice
# could change at the same time as my project lives)
# Detaisl of a course. Taking a course_id (by parameter of the route) and may take an error message and/or a success message to allow other function to call it easily
returnrender(request,'login.html',{'person':Person(name="",role=""),"error_message":"You must login!"})
# Getting up the person id (only the id part without person:)
person_id=getPersonId(person).split(":")[1]
# Getting all courses of the person (if he's a teacher, taking all courses where he's teacher ; if he's a student, taking all courses where he's registered to)
raiseHttp404("Course has expired... Get faster next time!")
# Tries to delete the course
res=delete_course(course_id)
ifnotres:
returncourses(request)
# Didn't delete the course, internal error
returncourses(request,"Internal error: Can't delete the course.")
# Publishing the delete of the course to all students registered to it (if it's their only course where they're registered to, it make go
# them back to courses to let them register to another course before getting access back to notifications page, because they're not registered to any course now)
publish(course_id,f"DELETE: Course:{course_id} has been deleted!")
# For each course, we add manually the id to it (without course:, by spliting it)
forcourseincourses_fetched:
course['id']=getCourseId(course).split(':')[1]
# If courses_fetched is empty, can't publish a message because a message published is associated to a course, so he must create a course before publishing a message
ifnotcourses_fetched:
#No course
returncourses(request,'You don\'t have any course. You must create one to publish a message!')
# Too many students registered to change the number of places that low
returnrender(request,'update.html',{'person':person,"error_message":"There's too many students registered to decrease the number of places that much!","course":course})
except:
# Number of places not an int
returnrender(request,'update.html',{'person':person,"error_message":"The number of places has not the right format!","course":course})
# This function is instantly called after notifications_view. How does my subscribe system work? Please referr to the README to understand it (cf. Section Pub/sub).
defnotifications(request):
# Getting the logged in person
person=request.session.get("person","")
ifperson=="":
# The user is not logged in. Go to login
returnrender(request,'login.html',{'person':Person(name="",role=""),"error_message":"You must login!"})
# Getting the messages from the session not to lose them when navigating
messages=request.session.get("messages",[])
ifperson=="":
# The user is not logged in. Go to login
returnrender(request,'login.html',{'person':Person(name="",role=""),"error_message":"You must login!"})
#Getting the person id
person_id=getPersonId(person)
ifnotperson_id:
# Person id not found.
returnrender(request,'notifications.html',{'person':person,'messages':messages,'error_message':'Internale error: person id not found.'})
# Getting the courses id where the person is registered to.
#When the person is not registered to any course, ask him to register to some course by redirecting him to courses page
# to make him know he's not registered to any course
returncourses(request,"You're not registered to any course. Please register to one to follow notifications.")
#Waiting for the message, for a new notification, subscribe to all of his courses channel.
message=get_message(*courses_id)
messages.append(message)
messages.reverse()
# When a message is received, adding it to the messages list at the beginning to get the latest message at the top of the page, and the oldest at the bottom
messages.insert(0,message)
# Updating the new messages list to the session to display it to the notifications view.
request.session["message"]=messages
returnnotifications_view(request)
# Clearing the notifications (the session messages)
defclear_notifications(request):
# Messages from session are now an empty list, clearing the list.
request.session["messages"]=[]
# Displaying again the notification view
returnnotifications_view(request)
# Every id is only the number, not the course:number or person:number