Skip to content

A mini I-learning application where professors can create Courses and publish Lessons in MarkDown and students can join these courses and study.

Notifications You must be signed in to change notification settings

LedjoLleshaj/ettore

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Ettore

Ettore is a platform for e-learning where there are two very distinct types of users: professors and students. Professors can create courses in which they can then upload lessons in Markdown format. In the other section, students can ask to join courses and, once accepted, are able to look at the lessons available in the courses.

Search course

Students

Lessons

Technology Stack

The project is written in Java 11 with the support of Gradle v6.3 as a build tool and Spring Boot as a web framework. The database that we decided to use is H2 because it is an in-memory database and, therefore, is very easy to use. The project follows a MVC pattern and it is divided in three main packages: controller, model and templates (which make up our view). On the front-end side, we're using TailwindCSS as a CSS framework and Thymeleaf as a template engine. For the testing phase we're using JUnit4 as the framework and Selenium specifically for end-to-end testing. The Bonigarcia web driver manager was also used to automatically install the necessary browser drivers. Lombok is a quality-of-life tool that spared us the effort of creating and maintaining boilerplate such as getters, setters, constructors, etc.

Development process

Initially, we conducted a meeting to discuss which of the proposed projects to develop and, eventually, we decided to go with the e-learning platform. Then, we started writing the user stories and further developed their most interesting subsections into more detailed scenarios as later explained below. Before starting with the actual development, we created a Figma board on which we sketched out a basic design for the project. This helped us to have a clear idea of how the project should look like. It also allowed us to better estimate the time required to develop each page in the project, making it easier to divide the work among the members of the team and assigning tasks to each member as time went on.

For managing the tasks, we created a Trello board where we divided the tasks and we assigned them to the members of the team. We also decided to use the GitFlow workflow for coordinating the development on the repository hosted on GitHub. We created a branch for each feature and we merged them into the develop branch once they were ready. During the development phase we used Discord to communicate with each other and to coordinate the work every time that we felt the need to do so. Each branch contains both the code and the tests that we wrote for that feature. For a branch to actually be merged into develop, a Pull Request had to be opened by the author of the branch, and all other members of the team had to review the code (possibly modifying or refactoring it) before finally approving the request. This ensured that every member was aware of the code being introduced in the codebase and could asses the quality of it.

CI/CD

To ensure the quality of our codebase at all times we made use of the free CI/CD platform provided by Github called Github Actions. This allowed us to run the tests automatically every time a new commit was pushed to the repository or a Pull Request was created. This way we could be sure that the code we were pushing was working and that it was not breaking the existing codebase, giving us the peace of mind that the code provided by each member of the team was working as expected. Combined with the Pull Requests workflow, this ensure a very high quality standard.

Requirements

The Ettore platform must allow the following actions:

  • User must be able to register and login on the platform as either a professor or a student

  • Professor:

    • Must be able to create, edit and delete courses
    • Must be able to create, edit and delete lessons written in the Markdown format
    • Must be able to approve or reject course subscription requests coming from students
    • Must be able to inspect the list of students subscribed to a course, and possibly remove them
  • Student:

    • Must be able to view the list of courses in which the student is enrolled in
    • Must be able to search for courses by title, description, year and category
    • Must be able to request to subscribe to a course
    • Must be able to view the lessons of the courses to which he/she is subscribed

User Stories

Story 1 - User registration, login and course creation (Gives rise to scenarios 1.1, 1.2 and 1.3)

Stefano is an ordinary professor and researcher at the Faculty of Computer Science of the University of Povegliano. Stefano can't wait for the semester to begin because he will teach a course on Signal analysis for the first time ever. Unfortunately, the University of Povegliano has not yet equipped itself with a system for e-learning, but Stefano would like to share with his students some in-depth articles that he made himself on the geometric interpretation of the Fourier and Zeta transformations. Sharing these contents via mail would be a feasible option, if only Stefano didn't wish to edit the shared texts after publishing them, and welcoming new students without having to continuously send mails. After some research on the web, Stefano discovers Ettore and decides to try it. He registers by entering his credentials (1.1). Then after logging in Ettore (1.2) he starts creating his course on the platform (1.3).

Story 2 - Course search, course subscription request and approval by the professor (Gives rise to scenarios 2.1, 2.2 and 2.3)

Filippo is a student at the Academy of Fine Arts of Pacengo, after having attended a talk organized by Dr. Stefano on low-level programming languages, and being fascinated by it, he decides to try to follow one of the courses taught by the same professor. Filippo unfortunately does not have the opportunity to follow Stefano's lessons because he is from another department but, after registering on Ettore, he manages to find, through the dedicated search page, one of the courses that interests him a lot (2.1): "High-performance neural networks in assembly". After entering the platform, Filippo subscribes to the course (2.2) of the professor. The professor, remembering the numerous interventions during the talk by Filippo, accepts the course subscription (2.3) to give Filippo the opportunity to explore this new discipline.

Story 3 - Lesson creation, viewing by students, and modification (Gives rise to scenario 3.1, 3.2 and 3.3)

Professor Stefano receives several emails from working students who were unable to follow the lesson in which the Fast Fourier Transformation was explained. In addition, many other students have shown great interest on the subject. Stefano, therefore, decides to take advantage of Ettore and publishes, on the page of his course "Signal analysis", several new lessons (3.1) about the topic. The lessons are in Markdown text format and this allows Stefano to enrich the content with images, videos, tables, mathematical formulas, etc. while remaining a simple text file. After publishing a lesson, the students enrolled in the course have the possibility to view it (3.2) . However later that day, Stefano realizes that one of the lessons had a typo in one of the formulas and decides to modify the lesson (3.3).

Scenarios

Scenario 1.1 - User registration

Init Assumption A person who is not yet registered on the online Ettore platform, decides to create their own account.
Normal Functioning The person goes on the Ettore homepage, displaying a login form. From this page he/she clicks on the "Register" button. At this point a form appears in which he/she is asked to enter the following information: first name, last name, email address, password, confirm password and, finally, whether to register as a professor or as a student. The selected mode will decide the actions that the user will be able to perform on the platform once registered. To send the form, the person presses the "Register" button. This is only active when: 1) All fields are filled 2) The email is in a valid format 3) The password is at least 8 characters long 4) The password and the confirm password match. The user is redirected to the page with the list of courses (courses taught if the user is a professor, otherwise subscribed courses) which will initially be empty.
What can go wrong There are one or more empty fields in the form. In this case the "Register" button cannot be pressed.

The email entered is not valid. For our purposes, an email address is considered valid if it has visible characters both to the left and to the right of an @ character. The check is done in real-time and the "Register" button is not clickable if the email does not satisfy the format just described.

The entered email has already been registered. In this case, the "Register" button can be pressed but then an error message is displayed afterwards. (This is because the front-end client cannot make that check by itself)

The passwords entered in the two fields do not match or they are too short (less than 8 characters). In this case the "Register" button cannot be pressed.
Other Activities At the same time, another person could be registering a user with the same email address. To one of the two actors (the one who arrives last) an error message will be shown as explained above.
Final System State There is a new registered user in the database.

Scenario 1.2 - User login

Initial assumption A person who is already registered on the Ettore platform, decides to log in.
Normal functioning The person goes to the Ettore login page. A form is shown in which he/she is asked to enter the following information: email address and password. To send the form, he/she presses the "Login" button. This is only active if the form is correctly filled in. i.e. 1) Both fields are non-empty, 2) The email has a valid format and 3) the password is at least 8 characters long. The user is redirected to the page with the list of courses (courses taught if the user is a professor, otherwise subscribed courses).
What can go wrong There are one or more empty fields in the form. In this case the "Login" button cannot be pressed.

The email entered is not valid. For our purposes, an email address is considered valid if it has visible characters both to the left and to the right of an @ character. The check is done in real-time and the "Login" button is not clickable if the email does not satisfy the format just described.

The password entered is too short (less than 8 characters). In this case the "Login" button cannot be pressed.
Other activities A user can log in at the same time with two different devices.
Final system state The user is logged in and can access the platform.

Scenario 1.3 - Creating a course

Initial assumption A user, who is logged-in on Ettore and has the "professor" role, would like to create a new course.
Normal functioning The user goes to the page where he can see the list of all his courses. At the beginning of the page there is a "New course" button that the user presses. A form opens in which the user must enter the fields: name, erogation year and category. Optionally, a description can also be added. The category is chosen from the following options: mathematics, sciences, history, geography, art, music, and languages. When the form is complete, the user presses the "Create course" button. This button cannot be pressed unless all the mandatory fields have been entered.
What can go wrong Some mandatory field has not been filled in. In this case, the "Create course" is not clickable.

There is already a course with the same name and taught in the same year. The button can be clicked but then the same page is displayed again and an error is shown. The form remains filled in and available to be corrected before trying again.
Other activities -
Final system state There is a new course and it is linked to the professor

Scenario 2.1 - Searching for a course

Initial assumption A user, who is logged-in on Ettore and has the "student" role, wishes to search for courses available on Ettore
Normal functioning The student navigates to his/her homepage, where the list of subscribed courses is displayed. He/She then clicks on the "Search course" button at the top and a page is shown displaying a couple of courses along with (initially empty) search filters. The user uses the search filters to refine the query.

The available filters are:
- A text input. Only courses that have that text (lowercased) in the title or description will be shown in the search results
- A dropdown that allows selecting a category. Only courses in that category will be shown
- A dropdown that allows selecting a year. Only courses erogated in that accademic year will be shown
What can go wrong If no courses match the given criteria (or if there are no registered courses), the user will be presented an empty list with a message that no courses were found.
Other activities At the same time that a student is searching for courses, a new one is created. New search requests after the creation of a course will display that course in the search results
Final system state -

Scenario 2.2 - Requesting to attend a course

Initial assumption A user, who is logged-in on Ettore and has the "student" role, wishes to apply for joining a new course that he/she has already found in the search page
Normal functioning The student, having found the course of interest with the search functionality, clicks the "Join" button. Once the enrollment request has been made, the student will be redirected to his list of joined courses but that doesn't include the wanted course just yet: the join request has to be accepted by the professor.
What can go wrong The "Join" button is disabled, this means that the request for enrollment in the course has already been submitted or the student is already enrolled in the course. In the former case, the student can do nothing but wait for feedback from the professor.
Other activities -
Final system state The course is linked to a new enrollment request from the student who performed the action.

Scenario 2.3 - Accepting the request from a student to join a course

Initial assumption A user, who is logged-in on Ettore and has the "professor" role, wishes to accept the request that a student has previously made to join one of his/her courses
Normal functioning The professor navigates to the "Manage" page for the course of interest. In the "Join requests" section there is a list of students waiting to be accepted into the course and another list of students who are already enrolled. Each list displays a row for each student. On the right of the row for a single join request there are two buttons: "Accept" and "Reject". The professor presses the "Accept" button and the page refreshes. The accepted student is now displayed in the list of enrolled students and the join request is no longer present.
What can go wrong If there are no students requesting to join, to the professor will be shown an empty list of requests. If there are many users making a request to join, the professor will have to manually confirm all the requests waiting for approval. A lot of manual work to do.
Other activities A student makes a request to join a course and the professor is on the "Manage" page. In this case the professor does not immediately see the new enrollment. Refreshing the page makes the new request appear.
Final system state On the student side there is a new course available in the list of joined courses. The student is now present among the enrolled students for the course.

Scenario 3.1 - Creating a new Lesson

Initial assumption A user, who is logged-in on Ettore and has the "professor" role, wishes to add a new lesson to one of his/her courses
Normal functioning The user goes to the page related to the course and navigates to the "Lessons" page and clicks on "New lesson" button. The application asks the professor for a title (mandatory), a description (optional) and the content (mandatory) which is expected to be in Markdown format. When at least the title and the content are present, the user can click on the "Save" button and a new lesson is created on the course.
What can go wrong The professor does not enter a title or the content of the lesson. In this case, the system will not allow the professor to save the lesson.

The professor enters a title that is already used by another lesson of the same course. In this case the system will append a "(1)" suffix at the end of the title to workaround the duplicated name.
Other activities A student is watching the list of lessons while the professor is adding a new one. In this case, the student cannot see the new lesson until he/she refreshes the page.
Final system state The lesson is linked to the course and can be viewed by the students enrolled in it.

Scenario 3.2 - Viewing a lesson

Initial assumption A user, who is logged-in on Ettore, wishes to view a lesson of one of the courses to which he/she has access
Normal functioning The user goes to the page related to the course and navigates to the "Lessons" section and clicks on the lesson he/she wants to view. The system shows the lesson's title, description and content which is rendered in markdown to show elements such as images, tables, lists, etc.
What can go wrong For students only: the lesson is deleted in-between the displaying of the list of lessons list and the clicking on a specific lesson. In this case, the student is redirected to the list of lessons and an error is shown on the screen.
Other activities The professor edits the content of the lesson while students are viewing it. In this case, the displayed content is unchanged until the page is refreshed.
Final system state -

Scenario 3.3 - Editing a lesson

Initial assumption A user, who is logged-in on Ettore and has the "professor" role, wishes to edit a lesson from one of his/her courses
Normal functioning The user goes to the page related to the course and navigates to the "Lessons" list section and clicks on the title of lesson he/she wants to edit. The system shows the lesson's title, description and rendered content. The professor clicks on the "Edit" button to go to the edit page of the lesson and is then able to change the title, description and content. When the professor is done editing the lesson he/she clicks on the "Save" button and the changes are saved.
What can go wrong The professor leaves an empty lesson title or content. In this case, the application will not allow the professor to save the lesson.

The professor enters a title that is already in use by a different lesson. In this case, the application will append a "(1)" at the end of the title to workaround the duplicate name.
Other activities Students are viewing the lesson as the professor is editing it. They will only see the changes when refreshing the page.
Final system state The edited lesson now contains the new information entered by the professor.

Authentication system

As the authentication framework provided by Spring was too complex for our needs and scope, we eventually decided to use our own homemade system.

Upon logging in, the user's client is sent a pair of cookies: one contains the email address and the other contains the hash of the password. These cookies are then sent along each following request. There is an Interceptor that sees every incoming request and checks: 1) if the cookies are present, 2) if a user whose email address matches the cookie exists and 3) if the password hashes match. In case of success, the request proceeds as normal and the actual handler can access the logged-in user object. Otherwise, a redirection to the login page is made. Logging out is performed simply by removing the cookies.

Code Quality Assurance

To guarantee a high code-quality stabdard we used automated tests. We used JUnit for orchestrating the tests and, additionally, Selenium for end-to-end testing. This allowed us to have a clear indication of the quality of our code and to improve it if necessary by periodically making improvements and refactorings to make the code more readable and maintainable.

In total we have 144 test divided in two main categories:

  • Unit tests: 58 These are mainly used to test the logic of our models, since that's were we check that some guarantees are always respected. For example, we have a test that checks that a student can not be accepted to join a course if he/she has never requested to join it.

  • End-to-End tests: 86 These are used to test the logic of our controllers and the whole interaction with the web application. These test cases emulate a user as he utilizes Ettore and performs various actions. Almost all E2E tests use the "Page Object" but a few exceptions were made to test paths that couldn't be tested otherwise. For instance: there is no way to go to the page for a non-existing course just by interacting with the application, you have to perform a specially crafted HTTP request.

Unit tests

Here is a list of the most important unit tests that we have implemented:

  • setEmailInvalidFormat: Tests that an IllegalArgumentException is thrown when the email is not in the correct format, as explained in the scenarios above.
  • setShortPassword: Tests that an IllegalArgumentException is thrown when the password is too short, as explained in the scenarios above.
  • setPassword: Tests that the password is correctly set by comparing the hashes.
  • addCourse: Tests that the course is correctly added to the list of courses of the professor.
  • removeCourse: Tests that the course is correctly removed from the list of courses of the professor.
  • requestCourseJoin: Tests that the user is correctly recorded among the students that desire to join the course.
  • approveCourseJoin: Tests that the user is correctly moved from the list of students that desire to join a course to the list of enrolled students.
  • rejectCourseJoin: Tests that the user is correctly removed from the list of students that desire to join the course.
  • removeStudentFromCourse: Tests that the student is correctly removed from the students enrolled in the course.
  • requestCourseJoinButAlreadyDid: Tests that an IllegalStateException is thrown when the student tries to request to join a course that he/she has already requested to join.
  • requestCourseJoinButAlreadyIn: Tests that an IllegalStateException is thrown when the student tries to request to join a course that he/she is already enrolled in.
  • approveCourseJoinButDidntAsk: Tests that an IllegalStateException is thrown when the professor tries to approve a student to join a course that he/she has not requested to join.
  • rejectCourseJoinButDidntAsk: Tests that an IllegalStateException is thrown when the professor tries to reject a student to join a course that he/she has not requested to join.
  • approveCourseJoinButAlreadyIn: Tests that an IllegalStateException is thrown when the professor tries to approve a student to join a course that he/she is already enrolled in.
  • rejectCourseJoinButAlreadyIn: Tests that an IllegalStateException is thrown when the professor tries to reject a student to join a course that he/she is already enrolled in.
  • removeStudentFromCourseButNotApprovedYet: Tests that an IllegalStateException is thrown when the professor tries to remove a student from a course that he/she is still waiting to be accepted into.
  • removeStudentFromCourseButNotIn: Tests that an IllegalStateException is thrown when the professor tries to remove a student from a course the he/she is not enrolled into and has not requested to join.

End2end tests

Here is a list of the most important E2E tests that we have implemented, grouped by the class or section they belong to:

    • allFieldsMustBeFilled: Tests that the user cannot register if he/she does not fill all the fields by continuously checking is the register button is enabled.
    • emailMustBeValid: Tests that the user cannot register if he/she does not fill the email field with a valid email address as explained in the scenarios.
    • passwordMustBeLongEnough: Tests that the user cannot register if he/she does not fill the password field with a password long enough (at least 8 characters, as explained in the scenarios)
    • passwordsMustMatch: Tests that the user cannot register if he/she does not enter the same password in both password fields.
    • cannotRegisterSameEmailTwice: Tests that the user cannot register if he/she enters an email that is already registered and tests that a proper error message is shown.
    • twoUsersCanHaveSamePassword: Tests that two users can have the same password.
    • registerStudent: Tests that a person can register him/her-self with the "student" role
    • registerProfessor: Tests that a person can register him/her-self with the "professor" role
    • login: Tests that the user can login if he/she enters the correct email and password.
    • noSuchUser: Tests that the user cannot login if he/she enters an email that is not registered and checks that a proper error message is shown.
    • wrongPassword: Tests that the user cannot login if he/she enters an existing email address but the wrong password and checks that a proper error message is shown.
    • canGoToLogin: Tests that the user can go to the login page if he/she is not logged in.
    • canGoToRegister: Tests that the user can go to the register page if he/she is not logged in.
    • cannotGoToSecurePages: Tests that the user cannot go to the secure pages if he/she is not logged in and that he/she will be redirected to the login page instead.
    • canGoToCoursesList Tests that the professor can go to the courses list page if he/she is logged in.
    • onceLoggedInCannotLoginAgain: Tests that the user cannot go to the login page if he/she is already logged in and that he/she will be redirected to the courses list page.
    • onceLoggedInCannotRegisterAgain: Tests that the user cannot go to the register page if he/she is already logged in and that he/she will be redirected to the courses list page.
    • canLogout Tests that the user can logout if he/she is logged in and checks that he/she gets redirected to the login page.
    • professorCannotGoToStudentSection: Tests that the professor cannot go to the student section and that he/she will be redirected to the courses list page.
    • studentCannotGoToProfessorSection: Tests that the student cannot go to the professor section and that he/she will be redirected to the courses list page.
      • badSearch: Tests that the application can handle a search request with badly formatted parameters and displays a proper error message.
      • displaysSomeCourses: Tests that the search page displays, by default, some courses (if there's any).
      • displaysNewCourse: Tests that a course that we haven't joined can be found using the search page.
      • canJoinNewCourse: Tests that the student can press the "Join" button to request to be accepted into the course
      • displaysRequestedCourse: Tests that courses for which the student has requested to join (but hasn't been accepted into just yet) are still shown in the search page
      • cantJoinRequestedCourse: Tests that the student is not able to press the "Join" button to request to be accepted into a course, if said course already has a pending request from the same student.
      • requestCourseJoin: Press the "Join" button and tests that the student is taken back to the list of courses, and that a new pending request is present.
      • displaysJoinedCourse: Tests that the courses for which the student is enrolled into are still shown in the search page.
      • cantJoinJoinedCourse: Tests that the student is not able to press the "Join" button to request to be accepted into a course, if he/she is already enrolled in said course.
      • crazyQuery: Tests what happens when a query matches no course. The list should be empty and a message should be displayed.
      • findCourseByCategory: Tests that courses with matching category are listed and the others are hidden.
      • findAnyCategory: Tests that the application really displays courses of various categories when the "Any" category is selected in the search filters.
      • findCourseByName: Tests that courses whose name includes the given query string are shown.
      • findCourseByNameIgnoreCase: Tests that courses whose name includes the given query string (ignoring the uppercase/lowercase differences) are shown.
      • findCourseByDescription: Tests that courses whose description includes the given query string are shown.
      • findCourseByStartingYear: Tests that the courses that start in the selected year are shown and the others are hidden.
      • findAnyYear: Tests that the application really displays courses of various starting year when the "Any" year is selected in the search filters.

Code Coverage

Coverage

About

A mini I-learning application where professors can create Courses and publish Lessons in MarkDown and students can join these courses and study.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published