diff --git a/README.md b/README.md
index 9681762..24cb780 100644
--- a/README.md
+++ b/README.md
@@ -40,10 +40,41 @@ Now, you can go into http://localhost:8000/redis to access the project.
Don't try to go into http://localhost:8000/admin, you won't have the rights and it's only used for development mode. This helps us to add/remove data to test quicker.
-## Features
+Once a user is registered to a course, he's automatically subscribed to courses update for all courses he follows into the notifications page.
-## TODO
+## Limitations
-- Publish a message
+This is a one-user app... You can't log in with 2 users.
+
+To test pub/sub, please follow these instructions:
+
+- `docker start redis-stack`
+
+- `docker exec -it redis-stack bash`
+
+- `redis-cli`
+
+- `psubscribe {id}` where the id is the course id where you will post messages from (with an update of the course or with Publish message). you can retrieve it with `KEYS course:*`
+
+- Update the course or post the message from this course id
+
+OR
+
+- `docker start redis-stack`
+
+- `docker exec -it redis-stack bash`
+
+- `redis-cli`
+
+- Go to your notifications after registered to a course (if you didn't you will get redirected to the courses page, where you can see that you don't follow any course)
+
+- `publish {id} {message}` where id is one of the course id followed by the current user, and message is your message
+
+---
+
+Expiration of the course: The course expires after 2 minutes.
+
+---
+
+Design is not existing yet.
-- Subscribe to course channel
\ No newline at end of file
diff --git a/redis_app/templates/courses.html b/redis_app/templates/courses.html
index 634fae2..e5e7102 100644
--- a/redis_app/templates/courses.html
+++ b/redis_app/templates/courses.html
@@ -9,6 +9,7 @@
CoursesProfileSearch
+ NotificationsLogout
{% endif %}
diff --git a/redis_app/templates/create.html b/redis_app/templates/create.html
index 5a31de5..ef7cbe2 100644
--- a/redis_app/templates/create.html
+++ b/redis_app/templates/create.html
@@ -9,6 +9,7 @@
CoursesProfileSearch
+ NotificationsLogout
{% endif %}
diff --git a/redis_app/templates/details.html b/redis_app/templates/details.html
index bb0a085..bb5f3ca 100644
--- a/redis_app/templates/details.html
+++ b/redis_app/templates/details.html
@@ -9,6 +9,7 @@
CoursesProfileSearch
+ NotificationsLogout
{% endif %}
diff --git a/redis_app/templates/home.html b/redis_app/templates/home.html
index de77586..f4c2d81 100644
--- a/redis_app/templates/home.html
+++ b/redis_app/templates/home.html
@@ -9,6 +9,10 @@
CoursesProfileSearch
+ Notifications
+ {% if person.role == 'Teacher' %}
+ Publish a message
+ {% endif %}
Logout
{% endif %}
diff --git a/redis_app/templates/login.html b/redis_app/templates/login.html
index 79e7c3c..0bd4d7c 100644
--- a/redis_app/templates/login.html
+++ b/redis_app/templates/login.html
@@ -9,6 +9,7 @@
CoursesProfileSearch
+ NotificationsLogout
{% endif %}
diff --git a/redis_app/templates/notifications.html b/redis_app/templates/notifications.html
new file mode 100644
index 0000000..6c2d9f1
--- /dev/null
+++ b/redis_app/templates/notifications.html
@@ -0,0 +1,28 @@
+
+
+
+
Notifications
+ {% for message in messages %}
+ {{ message.data }}
+ {% endfor %}
+ Clear notifications
+
+
+
\ No newline at end of file
diff --git a/redis_app/templates/profile.html b/redis_app/templates/profile.html
index 8337239..e34622b 100644
--- a/redis_app/templates/profile.html
+++ b/redis_app/templates/profile.html
@@ -9,6 +9,7 @@
CoursesProfileSearch
+ NotificationsLogout
{% endif %}
diff --git a/redis_app/templates/publish_message.html b/redis_app/templates/publish_message.html
new file mode 100644
index 0000000..603382e
--- /dev/null
+++ b/redis_app/templates/publish_message.html
@@ -0,0 +1,39 @@
+
+
+
+
+
+
diff --git a/redis_app/templates/register.html b/redis_app/templates/register.html
index 40664b2..0709d23 100644
--- a/redis_app/templates/register.html
+++ b/redis_app/templates/register.html
@@ -9,6 +9,7 @@
CoursesProfileSearch
+ NotificationsLogout
{% endif %}
diff --git a/redis_app/templates/search.html b/redis_app/templates/search.html
index 126233c..d25a4be 100644
--- a/redis_app/templates/search.html
+++ b/redis_app/templates/search.html
@@ -9,6 +9,7 @@
CoursesProfileSearch
+ NotificationsLogout
{% endif %}
diff --git a/redis_app/templates/update.html b/redis_app/templates/update.html
index 72c6e1c..61f9359 100644
--- a/redis_app/templates/update.html
+++ b/redis_app/templates/update.html
@@ -9,6 +9,7 @@
CoursesProfileSearch
+ NotificationsLogout
{% endif %}
diff --git a/redis_app/urls.py b/redis_app/urls.py
index 4ac9392..07f2008 100644
--- a/redis_app/urls.py
+++ b/redis_app/urls.py
@@ -15,6 +15,11 @@ urlpatterns = [
path('create_course_form', views.create_course_form, name='create_course_form'),
path('search', views.search_view, name='search'),
path('search_form', views.search_form, name='search_form'),
+ path('notifications_view', views.notifications_view, name='notifications_view'),
+ path('notifications', views.notifications, name='notifications'),
+ path('clear_notifications', views.clear_notifications, name='clear_notifications'),
+ path('publish_message', views.publish_message, name='publish_message'),
+ path('publish_message_form', views.publish_message_form, name='publish_message_form'),
path('course_register/', views.course_register_view, name='course_register'),
path('course_unregister/', views.course_unregister_view, name='course_unregister'),
path('update_course/', views.update_course_view, name='update_course'),
diff --git a/redis_app/views.py b/redis_app/views.py
index 4ff61c0..1c501e1 100644
--- a/redis_app/views.py
+++ b/redis_app/views.py
@@ -8,14 +8,36 @@ from .models import Person
DEFAULT_EXPIRE_TIME = 120
EXPIRE_REFRESH_TIME = 60
-COURSE_CHANNEL = "course"
-cancel = True
personLoggedIn = ""
+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 getCoursesFromPerson(person_id):
+ global redis_connection
+ course_keys = redis_connection.keys(f"course:*")
+ 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]
+ 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}")
@@ -58,9 +80,7 @@ def getCourseId(course):
def home(request):
- global cancel
global personLoggedIn
- cancel = True
return render(request, 'home.html', {'person': personLoggedIn})
def isPersonRegisteredToCourse(course):
@@ -91,9 +111,7 @@ def isPersonRegisteredToCourse(course):
def details(request, course_id, error_message=''):
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}")
@@ -111,8 +129,6 @@ def details(request, course_id, error_message=''):
def register(request):
global personLoggedIn
global redis_connection
- global cancel
- cancel = True
if request.method == 'POST':
person_name = request.POST['name']
person_role = request.POST['role']
@@ -134,8 +150,6 @@ def register(request):
def login(request):
global personLoggedIn
global redis_connection
- global cancel
- cancel = True
if request.method == 'POST':
person_name = request.POST['name']
person_role = request.POST['role']
@@ -151,55 +165,27 @@ def login(request):
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 = []
- 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]
- 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)
+ courses = getCoursesFromPerson(person_id)
return render(request, 'courses.html', {'person': personLoggedIn, 'courses': 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!"})
@@ -220,8 +206,6 @@ def change_profile(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})
@@ -267,6 +251,34 @@ def update_course(course_id, course_title, course_summary, course_level, course_
"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):
+ global personLoggedIn
+ courses_fetched = getCoursesFromPerson(getPersonId(personLoggedIn['name'], personLoggedIn['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': personLoggedIn, 'courses': courses_fetched})
+
+def publish_message_form(request):
+ global personLoggedIn
+ global redis_connection
+ if personLoggedIn == '':
+ return render(request, 'login.html', {'person': Person(name="", role=""), "error_message": "You must login!"})
+ if personLoggedIn['role'] == 'Student':
+ return render(request, 'publish_message.html', {'person': personLoggedIn, "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': personLoggedIn, "error_message": "The form has been wrongly filled!"})
def create_course_view(request):
global personLoggedIn
@@ -288,8 +300,6 @@ def update_course_view(request, course_id):
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':
@@ -306,8 +316,6 @@ def create_course_form(request):
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 == '':
@@ -422,18 +430,15 @@ def refresh_expire(course_id):
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))
+def publish(course_id, message):
+ global redis_connection
+ redis_connection.publish(course_id, message)
-# Courses is a list of coursee id to subscribe to
+# Courses is a list of course 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)
@@ -471,8 +476,6 @@ def search_view(request):
def search_form(request):
global personLoggedIn
global redis_connection
- global cancel
- cancel = True
if request.method == 'POST':
keywords = request.POST['keywords']
courses = []
@@ -486,5 +489,32 @@ def search_form(request):
return render(request, 'search.html', {'person': personLoggedIn, 'courses': courses})
return render(request, 'search.html', {'person': personLoggedIn, "error_message": "The form has been wrongly filled!"})
+
+def notifications_view(request):
+ global personLoggedIn
+ global messages
+ return render(request, 'notifications.html', {'person': personLoggedIn, 'messages': messages})
+
+def notifications(request):
+ global personLoggedIn
+ global messages
+ if personLoggedIn == "":
+ 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(personLoggedIn['name'], personLoggedIn['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
\ No newline at end of file