Live at: http://notency.netlify.com/
- Our Client
- Our Task
- Team Members
- Information Gathering
- Project Charter
- Interactions With Client
- Planning and Work Breakdown and Allocation
- User Stories
- Process Map
- Entity Relationship Diagram
- Wireframing
- Repositories
- Notency API
- Our Test Driven Development Pipeline
- NPM / Yarn Packages
- Client Questionaire
- Post Project Review
Embassy English is an organisation that seeks to provide teaching and resources for students learning English. They currently have schools in Australia, New Zealand, Canada, the UK and the USA.
Our client is looking for a web-based dashboard which would allow them to send notifications (via a service like SMS) and email announcements. The notifications are particularly critical, as they would allow the school to confirm the safety of their students in case of an emergency such as a flood. Since entering in each individual student into the system would be impractical, they would like to be able to upload student data via existing files, e.g. an excel spreadsheet containing the students' ids, names, phone numbers etc.
We set out to build a MERN stack application that would achieve these objectives.
Before our first meeting and based on the preliminary information provided by the client about the kind of app they wanted, we prepared a list of potential issues/questions to ask of the client.
Link to Issues/Questions to Ask the Client
Early on in the project, we prepared the Project Charter containing the problem statement, our proposed solution, the goal of the project, scope of the project, schedule/timeline and terms of the project. This client acknowledged and agreed with what we proposed and the signed document can be found here.
We corresponded with the client mainly via face to face meetings and email. We kept track of these interactions including emails and meeting minutes.
Link to all correspondence with Embassy English
We took an agile approach to organising our workflow, using Trello heavily to collect ideas and manage tasks.
We also used Trello to breakdown and allocate the workload based on our app's features and components.
Our approach to the project on a macro level was as follows:
-
Whilst we prepared the wireframes, Glenn worked on the basic functionality for the backend such as sending SMS and emails.
-
Once the wireframes were finalized, the rest of us figured out the components the application would have and developed each React component with styles and images needed by each component. These components could then be reused throughout the application. We ended up with a folder structure like this.
-
Once the components were in place, we assembled the various pages of the application using the individual components prepared in the previous step. Because we did this step, assembling was relatively easy and sort of like putting lego together. In taking this approach, we leveraged the strengths of React components to our benefit.
-
Finally, we connected the front end to the back end API and polished the UI and functionality.
As a member of staff in charge of checking students' safety in a critical event, I want to notify the students of an emergency so that important information and intructions can be conveyed on a timely basis
As a member of staff in charge of checking students' safety in a critical event, I want to track the replies of teachers and students (in particular, OK responses, non-OK responses and people who have no responded) so I can know if they are safe during an emergency
As a member of staff staff in charge of checking students' safety in a critical event, I want to let teachers/students of a certain nationality know if their country has suffered a calamity/accident/terrorist attack so that only relevant parties are communicated with
As a member of staff in charge of sending regular announcements to students, I want to send newsletters and announcements to students so I can conveniently
and efficiently keep students informed of non-critical events happening in the school
As a user of this app, I want to upload student and teacher data from a file so that I can easily keep the database mailing list updated without manual data entry and with minimum fuss.
As a member of staff staff in charge of checking students' safety in a critical event, I want to send the notifications in the form of a SMS message or an Email so that students have multiple means of receiving notifications and so that an internet connection is not required to receive that information
I want to easily search, group, categorize and manage the recipients so I can
easily look up whether students have been added to the mailing list
I want to identify the status of the notifications delivered so that I can
resend notifications to those that have not received them.
One-way communication is used for sending out Announcements. These announcements do not require the recipients to respond to the message.
Two-way communication is used for sending out Notifications. These notifications pertain to situations where the recipients need to respond to the message.
We made three iterant mock designs, building on the things we liked and the features that we thought worked best for this application. Links to each design can be found on our trello board.
We decided to separate the back-end repository from the front-end repository as it was an easier way to manage our branches. It also gave us a clearer perspective in finding our errors and keeping the changes under control.
The Notency API is the Node.JS back-end for Notency Front-end. Our back-end uses Express, Mongoose and other several open-source libraries. It was created with test driven development in mind while using tools such as Flow and Jest. It also uses the async/await syntax for all its routes and API calls.
Notency API makes use of Twilio for sending text messages and Mailgun for sending e-mail messages. These APIs are the foundation of our Notification and Announcement features and it completes the main essential of our app which is to have two-way communication with its customers.
- Git clone https://github.com/CosmoRocket/Notency-api.git
- Create a .env file containing the following keys in the root folder
JWT_SECRET =
JWT_ALGORITHM =
JWT_EXPIRES_IN =
TWILIO_ACCOUNT_SID =
TWILIO_AUTH_TOKEN =
TWILIO_NUMBER =
TWILIO_MESSAGING_SERVICE_SID =
MAILGUN_DOMAIN =
MAILGUN_API_KEY =
MAILGUN_PUB_KEY =
PUSHER_APP_ID =
PUSHER_KEY =
PUSHER_SECRET =
PUSHER_CLUSTER =
MONGO_URI =
MONGO_TEST_URI =
Notency API is built with 100% Test Driven Development coverage while the Notency Front-end is thoroughly tested across multiple mobile devices, desktop, and multiple browsers.
- Run syntax checker
yarn flow
yarn test
- Run tests
yarn jest
GET /users
- Get a list of all users
GET /users/:id
- Get a specific user
POST /users
- Create a new user
- Request Parameters:
username
,password
DELETE /users/:id
- Delete a specific user
POST /auth/register
- Register as a new user
- Request Parameters:
username
,password
POST /auth
- Sign in as an existing user
- Request Parameters:
username
,password
POST /recipients/search
- Search for a Recipient using filters
- Request Parameters:
nationality
,role
,graduationDate
e.g.
{
"nationality": "Australia",
"role": "Student",
"graduationDate": "31/12/2018",
"active": true
}
GET /recipients/active
- Get a list of all active recipients
GET /recipients
- Get a list of all recipients
GET /recipients
- Get a specific recipient
POST /recipients
- Create a new recipient
- Request Parameters:
idNo
,firstName
,lastName
,role
,mobile
,email
,nationality
,graduationDate
PATCH /recipients/:id
- Update a recipient
- Request Parameters:
idNo
,firstName
,lastName
,role
,mobile
,email
,nationality
,graduationDate
DELETE /recipients/:id
- Delete a specific recipient
GET /notifications/latest/:limit
- Get a number of latest notifications
- Request Parameter:
limit
e.g.
/notifications/latest/5
GET /notifications
- Get a list of all notifications
GET /notifications
- Get a specific notification
POST /notifications
- Create a new notification
- Request Parameters:
code
,subject
,body
,bodyHtml
,groups
,recipients
,responses
,createdAt
PATCH /notifications/:id
- Update a notification
- Request Parameters:
code
,subject
,body
,bodyHtml
,groups
,recipients
,responses
,createdAt
DELETE /notifications/:id
- Delete a specific notification
GET /announcements/latest/:limit
- Get a number of latest announcements
- Request Parameter:
limit
e.g.
/announcements/latest/5
GET /announcements
- Get a list of all announcements
GET /announcements
- Get a specific announcement
POST /announcements
- Create a new announcement
- Request Parameters:
subject
,bodyHtml
,groups
,recipients
,createdAt
PATCH /announcements/:id
- Update a announcement
- Request Parameters:
subject
,bodyHtml
,groups
,recipients
,createdAt``responses
,createdAt
DELETE /announcements/:id
- Delete a specific announcement
GET /messages
- Get a list of all messages
GET /messages
- Get a specific message
POST /messages
- Create a new message
- Request Parameters:
sender
,body
,createdAt
PATCH /messages/:id
- Update a message
- Request Parameters:
sender
,body
,createdAt
DELETE /messages/:id
- Delete a specific message
POST /sms/receive
- Receive SMS messages and store them in the Notification responses
- Request Parameters:
From
,Body
e.g.
{
From: '+61444888000',
Body: 'EQ1 OK'
}
POST /sms/send
- Send SMS messages to a single mobile number
- Request Parameters:
recipient
,message
e.g.
{
recipient: '+61444555555',
message: 'This is a test notification!'
}
POST /sms/groupSend
- Send SMS messages to multiple mobile numbers
- Request Parameters:
recipients
,message
e.g.
{
recipients: ['+61444555555', '+61444555552', '+61444555553'],
message: 'This is a test notification!'
}
POST /email/receive
- Receive E-mail messages and store them in the Notification responses
- Request Parameters:
sender
,subject
,stripped-text
e.g.
{
sender: 'somone@example.com',
subject: 'Re: Hello',
'stripped-text': 'EQ1 OK'
}
POST /email/send
- Send E-mail messages to a single or several e-mail addresses
- Request Parameters:
recipients
,subject
,text
,html
e.g.
{
recipients: ["someone@example.com"],
subject: "Hello",
text: "Testing some Mailgun awesomness!",
html: "<h1>Testing some Mailgun awesomness!</h1>"
}
POST /upload
- Upload a CSV file and store data as Recipients
- Request Parameters:
file
- mongoose
- express - Back end framework
- react - Front end view library
- body-parser - For parsing responses
- nodemon
- dotenv - For storing application environment variables
- axios - For making API requests from the front end
- twilio - Service we used for sending SMS and tracking responses
- mailgun / nodemailer - Service we used for sending emails and tracking responses
- exceljs
- nodemailer-mailgun-transport - Integrates nodemailer and mailgun for making sending emails with attachments easier
- react-draft-wysiwyg - Editor for composing emails.
- formik - Higher order component for storing and handling form state
- yup - Used for front end form validations
- immutable.js
- draft.js -
- flow - Type checking tool
- jest - Testing library
- multer - Middleware for processing FormData object and adding the req.file attribute
- moment - Formatting dates
- ramda - Library of higher order functions with emphasis on functional programming concepts
We prepared a questionnaire for Embassy English to ascertain the satisfaction with our product and service and we were grateful to have received very positive feedback for our product from our client.
Link to Completed Client Feedback Questionnaire
This project presented us with interesting features to implement such as sending SMS and tracking responses and therefore was a very valuable learning opportunity. Doing the project for a real client helped us train soft skills such as communication, planning, scoping to a minimum viable product, time management, getting feedback, etc.
The scope of the project was perfect for the time allowed whilst challenging at the same time.
We encountered quite a few technical challenges along the way. Below, we have documented our thought processes and how we resolved these issues.
We managed to communicate regularly with the client by email and meetings to get instructions and confirmation as we went along. We were very fortunate in that our client was very responsive to our questions and emails, which made the whole process a lot smoother for us.
We worked well together as a team and as a result were on schedule in completing the prototype of the application. We had regular discussions regarding the design, wireframes, approach, how to go about implementing features, etc. In doing so, we avoided replicating efforts.
We pair-programmed when appropriate which helped each other detect bugs in the program.
We used git and github mostly without much trouble, merging work as features were completed. There was just one time where we had trouble syncing our work due to a delayed pull but that was eventually resolved.
At one stage, one of the group members introduced a new library called Formik to manage form state in React which resulted in unfamiliar code being added to important parts of the codebase.
Introducing libraries made life easier on the one hand but was a challenge on the other hand especially when other team members tried to read and introduce other features on top of the existing code.
In these circumstances, it was important to have the person who introduced the new library to be involved in writing the new code by pair programming for example and to explain how the new library works.
One of the more time consuming and frustrating features to implement was dealing with all the edge cases of filtering recipients by group such as role, nationality, graduation date on the front end. Using the Chrome developer tools was extremely helpful in tracking the props and state changes in the app whilst developing this feature. So often, this was better than littering console.log
statements throughout the app.
A key feature of our app was being able to track SMS responses coming back from recipients. One of the challenges that came up during group discussions was how to associate SMS responses coming back with a particular notification given that SMS responses do not store any application state.
We considered two possible solutions:
-
Generate a short unique code for the student to type in which would link to a notification sent out
-
Have multiple numbers, where a notification is assigned a number and the numbers are rotated through as new notifications are created. The Sent number can then easily be connected to a notification.
We chose the first option as we could only test with one number with the trial TWilio account, though left open the possibility of adding more numbers through Twilio.
Getting email attachments to work was time consuming especially as we did not have a clear idea of what was involved in implementing the functionality. In the absence of clear guidance, documentation and tutorials, we had to take a step back and think through the whole process ourselves before implementing the feature. It was only by doing this that we could push through the knowledge gap and technical issues along the way.
After extensive research and multiple failed attempts, we learned that there are 3 main steps involved in sending an email with attachments: Submitting the file, saving the file to the app and attaching it in the email. We submitted the attachment using a FormData object and processed that on the back end using Multer. Multer would give us access to the file in req.file
and save that file to a temporary folder in the app, which could then be attached to the email.
The list of students/teachers at Embassy English gets updated quite frequently as new students enroll and existing students graduate on a weekly basis. One challenging decision we faced related to this was whether to totally replace all recipients in the database from the excel file or keep all users on the database and just activate/deactivate users.
Clarity came midway through the project when we realized the implications of deleting recipients from the database. This would mean some notifications would have IDs without corresponding recipients, as they would have been deleted. So this ruled out the option of deleting recipients.
On the other hand, if we were to let the list of recipients grow, students who had graduated or dropped out would still receive messages. The solution we decided to implement that made the most sense was to have a boolean attribute active
, which would be true
if the student was still on the list and false
if not. On the back-end, we would need to have a route such as /recipients/active
which would run a query to retrieve only "active" recipients.