The modern world feels like a very small place with six degrees of separation and social networking apps increasing visibility to everyone on its platform around the world. Yet why is it the case when we explore a new city for the first time, it is as if we are completely alone and left to make new connections by ourselves? Is there a better solution? iPair seeks to help individuals meet new people and engage in their activities together.
Goal: Get users off the app as quick as possible. 🤔
The point is not to squeeze as much capital out of each user and profit from obtrusive advertisements. Everything about the application is developed to be as intuitive, quick, and seamless as to not detract from your activities.
Front end:
Back end:
- AWS EC2 Ubuntu 18.04.6 LTS - Hosting
- Apache 2 HTTP Server - Web Server
- Gunicorn 'Green Unicorn' - Python Web Server Gateway Interface
- Flask Microframework - REST API
- PostgreSQL - Relational Database
Run this project within your own environment.
- Install Flutter - https://docs.flutter.dev/get-started/install
- Install PostgreSQL - https://www.postgresql.org/download/
- Install Python 3 - https://www.python.org/downloads/
-
Clone the repo
git clone https://github.com/vineet2420/Pair-Finder.git
or w/ ssh
git clone git@github.com:vineet2420/Pair-Finder.git
-
Change directories
cd pair-finder/flutter-ui/
-
Get dependencies
flutter pub get
-
Open
flutter-ui
folder with Android Studio or VSCode
-
Login
psql -U "username"
and follow password prompt
Make sure your PATH variable is correctly defined otherwise you will run into issues installing the python driver in the later steps
-
Create coredb database
CREATE DATABASE coredb;
-
Connect to coredb
\c coredb;
-
Create tables
4a. User Table
CREATE TABLE "user" (uid SERIAL PRIMARY KEY, first_name CHARACTER VARYING(50)[] NOT NULL, last_name CHARACTER VARYING(50)[] NOT NULL, email CHARACTER VARYING(255)[] NOT NULL, username CHARACTER VARYING(15)[] NOT NULL, password CHARACTER VARYING(64)[] NOT NULL, radius FLOAT);
Expected output: CREATE TABLE
4b. Activities Table
CREATE TABLE "activities" (aid SERIAL PRIMARY KEY, owner TEXT NOT NULL, act_name TEXT NOT NULL, act_desc TEXT NOT NULL, act_latitude FLOAT NOT NULL, act_longitude FLOAT NOT NULL, pair TEXT, time TEXT NOT NULL, address TEXT NOT NULL);
Expected output: CREATE TABLE
-
Change directory (from flutter-ui)
cd ../flask-server/
-
Edit secret.py
vim secret.py
2a. Add your postgres user's password to the return type of
getDbPass()
2b. Change the password salt to your own unique byte encoding in the
getSalt()
method -
Create a virtual environment (venv)
python3 -m venv ./venv
-
Activate the venv
source venv/bin/activate
-
Install the following packages
pip install flask
pip install flask-socketio
pip install gunicorn
pip install eventlet==0.30.2
pip install psycopg2
-
Run the server
gunicorn --worker-class eventlet -w 1 app:app
-
Copy the complete address and port the server is now listening on, most likely:
http://127.0.0.1:8000
-
Go back to the flutter-ui directory opened with Android Studio or VSCode in the installation step and open 📜constants.dart located under
📦 lib ┣ 📂 ActivityFlow ┣ 📂 Controlller ┗ 📜 activity_controller.dart ┗ 📜 activity_state_provider.dart ┗ 📜 auth_controller.dart ┗ 📜 constants.dart
-
Replace the String
host
variable from the aws ec2 address to the local web server address previously copied.
-
Go to https://cloud.google.com/docs/authentication/api-keys and get started with the Google Cloud Console API Dashboard.
-
Enable the following API services:
- Maps SDK for iOS
- Maps SDK for Android
- Maps JavaScript API
-
In the flutter-ui directory, add your API key to the following files:
Note: this is not the best practice for production applications but is suitable for our development environment
ios/Runner/AppDelegate.swift
(line: 13)android/app/src/main/AndroidManifest.xml
(line: 12)web/index.html
(line: 29)
User |
Details |
Activity |
Details |
---|---|---|---|
/login | Get an authenticated user's data. Args: email, password |
/activity/create | Post a new event. Args: owner, actname, actdesc, actlat, actlong, creationtime, actaddress |
/signup | Post a new user with all account data. Args: fname, lname, uname, email, password |
/activity/fetch | Get events within the client’s radius. Args: userlat, userlong, userradius |
/getuser | Get user data from a uid. Background task within caching layer after initial login to synchronize data changes from different platforms. Args: uid |
/activity/fetchGoing | Get all events the client is attending. Args: pair |
/activity/fetchSent | Get all events sent by the client. Args: owner |
||
/activity/addUser | Put the user into the request event, notify the event owner. Args: pair |
||
/activity/setRadius | Put the client’s radius to persist with login on multiple devices. Args: radius, uid |
- Haversine Formula used to retrieve the orthodromic distance between two latitude and longitude points.
- Server side: applied through query when fetching all the activities within the user’s radius. Calculate each distance, then check if the computed distance is ≤ the user’s radius.
- Client side: applied when another user creates an activity, the data payload is distributed to all connected devices.
- If the connected client is within the radius of the activity, it is added to the nearby activities list and displays a notification on the screen.
- Flask-SocketIO websocket implemented for full duplex communication over a single TCP connection.
- Client: Dart socket_io_client 1.0.2 which is a ported version of JavaScript Node.js library - Socket.io-client v2.0.1~v3.0.3
- Dart provider: ^6.0.1 package used to manage state of various events.
- ChangeNotifier implemented for a producer and consumer relationship.
- notifyListeners() called when producer needs to update all the consumers.
- Instances used –
- Adding Activity objects to nearByActivities, sentActivities, and attendingActivities lists.
- User adjusts radius which requires altering nearByActivities list.
- Transitioning to a new tab bar widget page outside of the class where the tab bar is defined without using callbacks.
For a more comprehensive deep dive into all of the functionality implemented: view these slides.
- Convert server side queries to postgres database into an Object Relational Mapping to prevent attacks
- Add an authentication layer between the client and server to prevent the API from being accessible to all unidentified requests
- Remove secret.py file containing sensitve information from version control (left in for your ease when duplicating this repo)
- Remove API keys from UI files
All credit during development stems from these extremely helpful resources which have cumulatively brought the application into its current stable state.
- Flutter API Reference Documentation
- Flask Documentation
- PostgreSQL 14.1 Documentation
- Amazon Elastic Compute Cloud Documentation
- Flask-SocketIO Documentation
- Green Unicorn Python WSGI HTTP Server Documentation
- Psycopg – PostgreSQL Database Adapter Documentation
- Dart Provider Package - Producer/Consumer Relationship Documentation