Reorganize repository

This commit is contained in:
rui hildt 2020-04-14 13:49:10 +02:00
commit 8bc11233e2
4 changed files with 690 additions and 0 deletions

29
README.md Normal file
View File

@ -0,0 +1,29 @@
## Meeting Planner Backend
Find the best time to schedule a meeting across several timezones.
Based on the availibity of all participants on specific days.
## How does it work?
#### For the meeting manager
1. Enter a title and description(optional)
2. Select the possible days on the calendar
3. Select a deadline for responses
4. Send invitations to potential participants
5. Wait either for all participants to respond or for the deadline (whichever happens first)
6. Choose and set the meeting date
Optional:
- Choose the minimum of people for the meeting to take place (quorum)
- Choose who needs to be there for the meeting to happen
#### For the participants
1. Select your availibility on the calendar: you can choose between (Yes / No / Ideal)
2. Wait for the meeting manager to confirm the meeting date
## Code and architecture
- [Backend](https://git.armada.digital/meeting-planner/backend)
- Frontend (coming soon)
- [API endpoints](./api-documentation.md)
- [Database design](https://dbdiagram.io/d/5e769ab14495b02c3b88936f)
- [Flowchart](https://app.diagrams.net/#Uhttps://git.armada.digital/meeting-scheduler-backend/)

637
api-documentation.md Normal file
View File

@ -0,0 +1,637 @@
## API Documentation
___
> Values required in **`bold`**.
### **Accounts** | `account`
| field | data type | metadata |
| :-------------| :---------------- | :-------------------------- |
| id | unsigned integer | primary key, auto-increment |
| username | varchar | required |
| email | varchar | required |
| password | varchar | required |
| timezone | varchar | |
| earliest_time | varchar | |
| latest_time | varchar | |
| created_at | datetime | generated by database |
#### Add an account
**`POST /api/accounts`**
##### Request
A json object for the account to register with **`username`**, **`email`**, **`password`**, `timezone`, `earliest_time` and `latest_time`.
```
{
"username": "jean",
"email": "jean@example.com",
"password": "really-strong-password",
"timezone": "Europe/Brussels",
"earliest_time":"09:30 AM",
"latest_time":"10:00 PM"
}
```
##### Response
A json object for the registered account with `id`, `username`, `email`, `timezone`, `earliest_time` and `latest_time`.
```
{
"status": 201,
"data": {
"id": 1,
"username": "jean",
"email": "jean@example.com",
"timezone": "Europe/Brussels",
"earliest_time":"09:30 AM",
"latest_time":"10:00 PM"l
}
}
```
#### Log in an account
**`POST /api/accounts/login`**
##### Request
A json object for the account to login with either **`email`** or **`username`**, and **`password`** .
```
{
"username": "jean",
"password": "super-strong-password"
}
```
##### Response
A json object for the registered account with `id`, `username`, `email`, `timezone`, `earliest_time` and `latest_time`.
```
{
"status": 201,
"data": {
"id": 1,
"username": "jean",
"email": "jean@example.com",
"timezone": "Europe/Brussels",
"earliest_time":"09:30 AM",
"latest_time":"10:00 PM"l
}
}
```
#### Update an account
**`PUT /api/accounts/:id`**
##### Request
A json object for the account to register with `id` and any of `username`, `email`, `password`, `timezone`, `earliest_time`, `latest_time`.
```
{
"id": 15,
"earliest_time": "08:30 AM",
"latest_time": "08:00 PM"
}
```
##### Response
A json object for the registered account with `id`, `username`, `email`, `timezone`, `earliest_time` and `latest_time`.
```
{
"status": 200,
"data": {
"id": 1,
"username": "jean",
"email": "jean@example.com",
"timezone": "Europe/Brussels",
"earliest_time": "09:30 AM",
"latest_time": "10:00 PM"
}
}
```
#### Delete an account
**`DELETE /api/accounts/:id`**
##### Request
A json object for the account to delete with `id`.
```
{
"id": 15
}
```
##### Response
A json object for the deleted account with status code, message, `id`, `username` and `email`.
```
{
"status": 200,
"message": " '`username`' account with email '`email`' has been successfully deleted."
"data": {
"id": 15,
"username": "jean",
"email": "jean@example.com"
}
}
```
#### Get a list of meetings for an account
**`GET /api/accounts/:account_id/meetings`**
##### Request
A json object with an `account_id`.
```
{
"id": 5
}
```
##### Response
A json object for the specified account with an array of `meeting`.
```
{
"status": 200,
"data": [{
"id": "worldwide-strategy-meeting-for-11059",
"title": "Worldwide strategy meeting for growth",
"description": "Let's find the best ethical growth hacking technics together. Yeah, fun.",
"start_time": "2022-02-16 20:00:00",
"timezone": "Europe/Brussels",
"duration": 60,
"status": 1
},
{
"id": "follow-up-with-tech-team-21850",
"title": "Follow up with tech team",
"duration": 120,
"status": 0
}
]
}
```
### **Meetings** | `meeting`
| field | data type | metadata |
| :---------- | :-------- | :-------------------------------------------------------------------------------- |
| id | varchar | primary key, first 30 chars of `title` with words separated by `-` + 5 random int |
| title | varchar | required |
| description | varchar | |
| start_time | datetime | |
| timezone | varchar | |
| duration | int | required |
| status | boolean | required: `0` (proposed) or `1` (confirmed) |
| password | varchar | |
| created_at | datetime | generated by database |
#### Add a meeting
**`POST /api/meetings`**
##### Request
A json object for the meeting to add with **`id`**, **`title`**, `description`, `start_time`, **`duration`** and `password`.
```
{
"id": "worldwide-strategy-meeting-for-11059",
"title": "Worldwide strategy meeting for growth",
"description": "Let's find the best ethical growth hacking technics together. Yeah, fun.",
"duration": 90,
"password": "generic-password"
}
```
##### Response
A json object for the added meeting with `id`, `title`, `description`, `start_time`, `duration`, `status` and `password`.
```
{
"status": 201,
"data": {
"id": "worldwide-strategy-meeting-for-11059",
"title": "Worldwide strategy meeting for growth",
"description": "Let's find the best ethical growth hacking technics together. Yeah, fun.",
"duration": 90,
"status": 0
}
}
```
#### Update a meeting
**`PUT /api/meetings/:id`**
##### Request
A json object for the meeting to update with `id`, `acount_id` and any of `title`, `description`, `start_time`, `timezone`, `duration`, `status` and `password`.
```
{
"id": "worldwide-strategy-meeting-for-11059",
"account_id": 5,
"start_time": "2022-02-16 20:00:00",
"timezone": "Europe/Brussels",
"status": 1
}
```
##### Response
A json object for the updated meeting with `id`, `title`, `description`, `start_time`, `timezone`, `duration` and `status`.
```
{
"status": 200,
"data": {
"id": "worldwide-strategy-meeting-for-11059",
"title": "Worldwide strategy meeting for growth",
"description": "Let's find the best ethical growth hacking technics together. Yeah, fun.",
"start_time": "2022-02-16 20:00:00",
"timezone": "Europe/Brussels",
"duration: 90,
"status": 1
}
}
```
#### Delete a meeting
**`DELETE /api/meetings/:id`**
##### Request
A json object for the meeting to delete with `id` and `account_id`.
```
{
"id": "worldwide-strategy-meeting-for-11059",
"account_id": 5
}
```
##### Response
A json object for the deleted meeting with status code and message.
```
{
"status": 200,
"message": "Meeting '`meeting_title`' has been successfully deleted."
}
```
#### Get a list of all participants for a meeting
**`GET /api/meetings/:id/participants`**
##### Request
A json object with an `account_id`.
```
{
"id": "worldwide-strategy-meeting-for-11059",
"account_id": 5
}
```
##### Response
A json object for the specified meeting with an array of `participant`.
```
{
"status": 200,
"data": [{
"id": 23,
"account_id": 5,
"meeting_id": "worldwide-strategy-meeting-for-11059",
"earliest_time": "09:30 AM",
"latest_time": "05:00 PM",
"quorum": 1,
"mandatory": 1,
"host": 1,
"answered": 1,
"timezone": "Europe/Brussels"
},
{
"id": 28,
"account_id": 11,
"meeting_id": "worldwide-strategy-meeting-for-11059",
"earliest_time": "10:00 AM",
"latest_time": "09:00 PM",
"quorum": 0,
"mandatory": 1,
"host": 0,
"answered": 1,
"timezone": "Europe/Brussels"
}
]
}
```
#### Get a list of complete availibility for a meeting
**`GET /api/meetings/:id/availibility`**
##### Request
A json object with a `meeting_id`.
```
{
"id": "worldwide-strategy-meeting-for-11059"
}
```
##### Response
A json object for the specified meeting with an array of `participant`.
```
{
"status": 200,
"data": {
"meeting_id": "worldwide-strategy-meeting-for-11059",
"availibility": [{
"participant_id": 5,
"possible_date_id": 21,
"intervals": [{
"preference": 0,
"start_time": "2021-06-25 09:00:00",
"end_time": "2021-06-25 13:00:00",
"timezone": "Europe/Brussels"
},
{
"preference": 1,
"start_time": "2021-06-25 15:00:00",
"end_time": "2021-06-25 20:00:00",
"timezone": "Europe/Brussels"
}]
},
{
"participant_id": 56,
"possible_date_id": 21,
"intervals": [{
"preference": 0,
"start_time": "2021-06-25 08:00:00",
"end_time": "2021-06-25 10:30:00",
"timezone": "Europe/Brussels"
}]
}
]
}
}
```
### **Possible Dates** | `possible_date`
| field | data type | metadata |
| :------------ | :-------- | :-------------------------- |
| id | int | primary key, auto-increment |
| meeting_id | varchar | foreign key, required |
| possible_date | date | foreign key, required |
#### Add a possible date
**`POST /api/possible-dates`**
##### Request
A json object for the meeting to add with **`meeting_id`** and **`possible_date`**.
```
{
"meeting_id": "worldwide-strategy-meeting-for-11059",
"possible_date": "2020-02-18"
}
```
##### Response
A json object for the added meeting with `id`, `meeting_id` and `possible_date`.
```
{
"status": 201,
"data": {
"id": 5,
"meeting_id": "worldwide-strategy-meeting-for-11059",
"possible_date": "2020-02-18"
}
}
```
#### Delete a possible date
**`DELETE /api/possible-dates/:id`**
##### Request
A json object for the possible date to delete with `id` and `account_id`.
```
{
"id": 12,
"account_id": 5
}
```
##### Response
A json object for the deleted possible date with status code and message.
```
{
"status": 200,
"message": "Date '`possible_date`' has been successfully removed for the '`meeting_title`'."
}
```
### **Participants** | `participant`
| field | data type | metadata |
| :------------ | :-------- | :-------------------------- |
| account_id | int | primary key, auto-increment |
| meeting_id | varchar | required |
| earliest_time | datetime | |
| latest_time | datetime | |
| quorum | boolean | `0` (no) or `1` (yes) |
| mandatory | boolean | `0` (no) or `1` (yes) |
| host | boolean | `0` (no) or `1` (yes) |
| answered | boolean | `0` (no) or `1` (yes) |
| timezone | varchar | required |
| created_at | datetime | generated by database |
#### Invite a participant
**`POST /api/participants`**
##### Request
A json object for the participant to add with **`account_id`**, **`meeting_id`**, **`earliest_time`**, **`latest_time`**, **`quorum`**, **`mandatory`**, **`host`**, **`answered`** and **`timezone`**.
```
{
"account_id": 5,
"meeting_id": "worldwide-strategy-meeting-for-11059",
"earliest_time": "08:30 AM",
"latest_time": "08:00 PM",
"quorum": 0,
"mandatory": 1,
"host": 0,
"answered": 0,
"timezone": "Europe/Brussels"
}
```
##### Response
A json object for the participant with `id`, `account_id`, `meeting_id`, `earliest_time`, `latest_time`, `quorum`, `mandatory`, `host`, `answered` and `timezone`.
```
{
"status": 201,
"data": {
"id": 23,
"account_id": 5,
"meeting_id": "worldwide-strategy-meeting-for-11059",
"earliest_time": "08:30 AM",
"latest_time": "08:00 PM",
"quorum": 0,
"mandatory": 1,
"host": 0,
"answered": 0,
"timezone": "Europe/Brussels"
}
}
```
#### Update a participant
**`PUT /api/participants/:id`**
##### Request
A json object for the participant to invite with `id` and any of `id`, `earliest_time`, `latest_time`, `quorum`, `mandatory`, `host`, `answered` and `timezone`.
```
{
"account_id": 5,
"earliest_time": "09:30 AM",
"latest_time": "05:00 PM",
"quorum": 1,
"host": 1,
"answered": 1,
}
```
##### Response
A json object for the participant with `id`, `account_id`, `meeting_id`, `earliest_time`, `latest_time`, `quorum`, `mandatory`, `host`, `answered` and `timezone`.
```
{
"status": 200,
"data": {
"id": 23,
"account_id": 5,
"meeting_id": "worldwide-strategy-meeting-for-11059",
"earliest_time": "09:30 AM",
"latest_time": "05:00 PM",
"quorum": 1,
"mandatory": 1,
"host": 1,
"answered": 1,
"timezone": "Europe/Brussels"
}
}
```
#### Delete a participant
**`DELETE /api/participants/:id`**
##### Request
A json object for the participant to delete with `id`.
```
{
"id": 23
}
```
##### Response
A json object for the deleted participant with status code and message.
```
{
"status": 200,
"message": "The '`participant_username`' has been successfully removed from the meeting '`meeting_title`'."
}
```
### **Availibility** | `availibility`
| field | data type | metadata |
| :--------------- | :-------- | :--------------------------------- |
| id | int | primary key, auto-increment |
| participant_id | int | foreign key, required |
| possible_date_id | int | foreign key, required |
| preference | boolean | `0` (ideal) or `1` (yes), required |
| start_time | timestamp | required |
| end_time | timestamp | required |
| timezone | varchar | required |
| created_at | datetime | generated by database |
GET / DELETE / UPDATE
#### Add an availibility for a possible date
**`POST /api/availibility`**
##### Request
A json object with the availibility to add with **`participant_id`**, **`possible_date_id`** and an array of intervals with **`preference`**, **`start_time`**, **`end_time`** and **`timezone`**.
```
{
"participant_id": 5,
"possible_date_id": 21,
"intervals": [{
"preference": 0,
"start_time": "2021-06-25 09:00:00",
"end_time": "2021-06-25 13:00:00",
"timezone": "Europe/Brussels"
},
{
"preference": 1,
"start_time": "2021-06-25 15:00:00",
"end_time": "2021-06-25 20:00:00",
"timezone": "Europe/Brussels"
}
]
}
```
##### Response
A json object with the availibility to add with **`participant_id`**, **`possible_date_id`** and an array of intervals with **`id`**, **`preference`**, **`start_time`**, **`end_time`** and **`timezone`**.
```
{
"status": 201,
"data": {
"participant_id": 5,
"possible_date_id": 21,
"intervals": [{
"id": 45,
"preference": 0,
"start_time": "2021-06-25 09:00:00",
"end_time": "2021-06-25 20:00:00",
"timezone": "Europe/Brussels"
},
{
"id": 46,
"preference": 1,
"start_time": "2021-06-25 09:00:00",
"end_time": "2021-06-25 20:00:00",
"timezone": "Europe/Brussels"
}
]
}
}
```
#### Delete availibility for a possible date
**`DELETE /api/availibility/:id`**
##### Request
A json object for the availibility to delete with **`participant_id`** and **`possible_date_id`**.
```
{
"participant_id": 5,
"possible_date_id": 21
}
```
##### Response
A json object for the deleted availibility with status code and message.
```
{
"status": 200,
"message": "Availibility successfully deleted for `participant_username` on `possible_date`"
}
```

1
meetingscheduler.drawio Normal file
View File

@ -0,0 +1 @@
<mxfile host="Electron" modified="2020-03-25T18:31:26.977Z" agent="Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/12.6.5 Chrome/80.0.3987.141 Electron/8.1.1 Safari/537.36" etag="QzSuGUuUtPoOGiynkDqc" version="12.6.5" type="device"><diagram id="C5RBs43oDa-KdzZeNtuy" name="App flow">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</diagram></mxfile>

23
research.md Normal file
View File

@ -0,0 +1,23 @@
## Proposed Tech stack
- NodeJS
- Express
- Postgresql
- Luxon
## How to deal with date and time in PostgreSQL?
- use `datetime` datatype without timezone (datetime with timezone is using offset, which doesn't work well with DST)
- store datetime in UTC
- add timezone information separately, use `Full time zone name` (human friendly)
- use `ISO 8601` format, which is human friendly to read and supported by every library
See:
- https://www.moesif.com/blog/technical/timestamp/manage-datetime-timestamp-timezones-in-api/
- https://www.postgresql.org/docs/current/datatype-datetime.html
## Inspiration
- Framadate: https://framadate.org/
- Omnipointment: https://omnipointment.com
- When is Good: http://whenisgood.net/
- Xoyondo: https://xoyondo.com/