Skip to content

robsdevcraft/birddex

Repository files navigation

🐤 Bird Dex 🐦

Team:

  • Robert Anderson - Team Lead - Point of contact for Galvanize staff, Tracker Admin
  • Brian Stormes - Design Lead - Theming + GHI Design, Approves styles used by the team
  • Denny Bucklin - Backend Lead - Developing API + Model Architecture, Verifies best practices before team pushes
  • Phil Bunker - Docs Lead - Establish Docs formatting standards, Approves Docs from team

Index



Getting Started 🐣

Top

To use the live app, simply visit: https://canarydevs.gitlab.io/birddex

If you would like to run your own local instance, follow these steps:

(Make sure you have Docker, Git, and Node.js 18.2 or above)

  1. Visit our Gitlab: https://gitlab.com/canarydevs/birddex

  2. Fork and clone the repository onto your local computer.

  3. Create a file named .env in the root directory and add the following lines:

PSQL_PASSWORD=postgresql
PSQL_USER=postgresql
PSQL_DB=postgresql
DATABASE_URL=postgresql://postgresql:postgresql@db/postgresql
SIGNIN_KEY={Insert a random string here}
VITE_API_HOST=localhost
CORS_HOST=http://localhost:5173
GOOGLE_API_KEY={API key goes here}
NUTHATCH_API_KEY={API key goes here}
EBIRDS_API_KEY={API key goes here}
PUBLIC_URL=''
API_HOST=http://localhost:8000
  1. Edit the file .gitlab-ci.yml and comment out all lines from build-api-image: to the end of the file. These lines are used for live deployment.

  2. Build and run the project using Docker with these commands:

docker volume create birddex-db
docker-compose build
docker-compose up
  • After running these commands, make sure all of your Docker containers are running
  • View the project in the browser: http://localhost:5173/


Design 🦅

Top


Description

This bird watching app is comprised of three main pages:

The landing page serves as a gateway for users to sign up or sign in. When signing up, a user must specify an email address, which will be used as their user name.

Once logged in, the dashboard features two carousels – one showcasing birds known to the Nuthatch API, and the other displaying birds with local sightings. The dashboard also includes an accordion list of all bird sightings made by the user.

The "All Birds" page presents a grid of all bird species within the app, visually highlighting those observed. Users can access a list of their observations by selecting a specific bird. An "Add Sighting" button is available on each image to allow the user to record a sighting via a popup modal. When adding a sighting, users have the option to indicate the date and location of the sighting and provide a url to an image.

The app aims to enhance your bird watching experience by efficiently organizing sightings and providing useful species information.


Screenshots

Landing Page:

Landing Page Screenshot

Dashboard:

Dashboard Screenshot

Birds Page:

Birds Page Screenshot

Updating a Sighting:

Birds Page Screenshot


Front End Wireframe

Front End Wireframe


Project Diagram

Project Diagram


Entity Relationship Diagram

Entity Relationship Diagram


Definition of Terms 🐦

Users

Stored as a record in the users table. User is expected to use their email address as their login name, which has a Unique constraint to prevent duplicate usernames. First name, last name and location are recorded on user sign up. Geocoding is performed on user creation to record latitude and longitude for the user for the "Nearby Sightings" feature utilizing the eBirds API.

Birds

Each bird is a record in the birds table, pulled via a poller from the Nuthatch API. Each record includes: common name, scientific name, an unsplash image url, and conservation status.

Sightings

A sighting is a recorded observation of a bird, with details such as date, location, bird type, and (optionally) a user-submitted image of their sighting. A user may have many sightings, and a bird may have many sightings.

Observations

Recent observation data is pulled in real time from the eBirds API to be displayed on the landing page carousels. If a logged in user has provided their location, then eBirds observations within a 50km radius will be displayed. Otherwise, the most recent observations available from eBirds will be used.



Database Schemas 🦉

Top

Users
    CREATE TABLE users (
        id SERIAL PRIMARY KEY NOT NULL,
        email VARCHAR(100) NOT NULL UNIQUE,
        hash_password VARCHAR(100) NOT NULL,
        first_name VARCHAR(100) NOT NULL,
        last_name VARCHAR(100) NOT NULL,
        location VARCHAR(100) NOT NULL,
        lat FLOAT NOT NULL,
        lng FLOAT NOT NULL
    )
Birds
    CREATE TABLE birds (
        id SERIAL PRIMARY KEY NOT NULL,
        common_name VARCHAR(100) UNIQUE NOT NULL,
        sci_name VARCHAR(100) NOT NULL,
        conservation_status VARCHAR(100) NOT NULL,
        image VARCHAR(200) NOT NULL
    )
Sightings
    CREATE TABLE sightings (
        id SERIAL PRIMARY KEY NOT NULL,
        date DATE NOT NULL,
        location VARCHAR(100) NOT NULL,
        lat FLOAT NOT NULL,
        lng FLOAT NOT NULL,
        image VARCHAR(200) NOT NULL,
        bird_id INTEGER REFERENCES birds(id) NOT NULL,
        user_id INTEGER REFERENCES users(id) NOT NULL
    )


Local API Descriptions 🦆

Top

User Actions

Action Method URL
Create User POST /api/users
Login POST /token
Check User Status GET /token
Logout DELETE /token

User Actions Diagram:

Birddex Users Diagram

Create User:
Example Request:
URL: /api/users/
Method: POST
{
  "email": "user1@example.com",
  "password": "hash1",
  "first_name": "Emily",
  "last_name": "Johnson",
  "location": "Miami, FL"
}
Example Response:
{
  "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiJlOGNjYzQxYS0yYmMzLTRlZjAtYWM5Yy0xMmUyNDZmYzAzOWMiLCJleHAiOjE3MDc0OTI5NjYsInN1YiI6InVzZXI4QGV4YW1wbGUuY29tIiwiYWNjb3VudCI6eyJpZCI6MTEsImVtYWlsIjoidXNlcjhAZXhhbXBsZS5jb20iLCJmaXJzdF9uYW1lIjoiRW1pbHkiLCJsYXN0X25hbWUiOiJKb2huc29uIiwibG9jYXRpb24iOiJNaWFtaSwgRkwiLCJsYXQiOjI1Ljc2MTY3OTgsImxuZyI6LTgwLjE5MTc5MDJ9fQ.ea5Le-zpWg-lqehl-hLL-AWWpK-QdHRYVooowWZ3Gw0",
  "token_type": "Bearer",
  "user": {
    "id": 1,
    "email": "user1@example.com",
    "first_name": "Emily",
    "last_name": "Johnson",
    "location": "Miami, FL",
    "lat": 25.7616798,
    "lng": -80.1917902
  }
}
User Login:
Example Request:
URL: /token
Method: POST
Content-Type: application/x-www-form-urlencoded
email=user1@example.com&password=hash1
Example Response:
{
  "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiI5OTQwZjYzYi1mNGJiLTQwMDgtYjVmYi01OWRiYmU1ZTllMmMiLCJleHAiOjE3MDc0OTI1NDgsInN1YiI6InVzZXIxQGV4YW1wbGUuY29tIiwiYWNjb3VudCI6eyJpZCI6MSwiZW1haWwiOiJ1c2VyMUBleGFtcGxlLmNvbSIsImZpcnN0X25hbWUiOiJFbWlseSIsImxhc3RfbmFtZSI6IkpvaG5zb24iLCJsb2NhdGlvbiI6Ik1pYW1pLCBGTCIsImxhdCI6MjUuNzYxNjc5OCwibG5nIjotODAuMTkxNzkwMn19.gKT5lxFnE-vLtHEg9W7-79iE9sSRjV6jOTXyXkFhxB4",
  "token_type": "Bearer"
}
Check User Status:
Example Request:
URL: /token
Method: GET
Example Response:

Returns null if no user, or:

{
  "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiJiZmI5YTAwNy04ZDlhLTRjZTMtOWIzNy0yM2I0ZTgzNjdmYWMiLCJleHAiOjE3MDc0OTMyMTksInN1YiI6InVzZXIxQGV4YW1wbGUuY29tIiwiYWNjb3VudCI6eyJpZCI6MSwiZW1haWwiOiJ1c2VyMUBleGFtcGxlLmNvbSIsImZpcnN0X25hbWUiOiJFbWlseSIsImxhc3RfbmFtZSI6IkpvaG5zb24iLCJsb2NhdGlvbiI6Ik1pYW1pLCBGTCIsImxhdCI6MjUuNzYxNjc5OCwibG5nIjotODAuMTkxNzkwMn19.5B3XkTQwY42wIoGsj6oampK6G6iai1vl2xxiWb-_kRY",
  "token_type": "Bearer",
  "user": {
    "id": 1,
    "email": "user1@example.com",
    "first_name": "Emily",
    "last_name": "Johnson",
    "location": "Miami, FL",
    "lat": 25.7616798,
    "lng": -80.1917902
  }
}
User Logout:
Example Request:
URL: /token
Method: DELETE
Example Response:
true

Birds Actions

Action Method URL
Create Bird POST /api/birds
List Birds GET /api/birds
Get Bird GET /api/birds/{id}
Update Bird PUT /api/birds/{id}
Delete Bird DELETE /api/birds/{id}
Create Bird:
Example Request:
URL: /api/birds/
Method: POST
{
  "common_name": "Snow Goose",
  "sci_name": "Anser caerulescens",
  "conservation_status": "Low Concern",
  "image": "https://images.unsplash.com/photo-1643650997626-0124dbb98261"
}
Example Response:
{
  "id": 1,
  "common_name": "Snow Goose",
  "sci_name": "Anser caerulescens",
  "conservation_status": "Low Concern",
  "image": "https://images.unsplash.com/photo-1643650997626-0124dbb98261",
  "sightings": null
}
List Birds:

To receive information about a specific bird, supply common_name or sci_name. Otherwise, all birds are returned. If a user is signed in, then an array of their sightings is included with the bird information.

Example Request:
URL: /api/birds/
Method: GET
{
  "common_name": "Snow Goose",
  "sci_name": ""
}
Example Response:
[
  {
    "id": 2,
    "common_name": "Snow Goose",
    "sci_name": "Anser caerulescens",
    "conservation_status": "Low Concern",
    "image": "https://images.unsplash.com/photo-1542252223-c7f5b1142f93",
    "sightings": [
      {
        "id": 40,
        "date": "2024-02-03",
        "location": "South Beach, FL",
        "lat": 25.7826123,
        "lng": -80.1340772,
        "image": "https://images.unsplash.com/photo-1542252223-c7f5b1142f93"
      }
    ]
  }
]

Sightings Actions

Action Method URL
Create Sighting POST /api/users/{user_id}/sightings
List Sightings GET /api/users/{user_id}/sightings
Show Sighting GET /api/users/{user_id}/sightings/{id}
Update Sightings PUT /api/users/{user_id}/sightings/{id}
Delete Sighting DELETE /api/users/{user_id}/sightings/{id}
Create sighting:
Example Request:
URL: /api/users/{user_id}/sightings/
Method: POST
{
  "date": "2024-02-09",
  "location": "North Beach, FL",
  "image": "https://images.unsplash.com/photo-1542252223-c7f5b1142f93",
  "bird_id": 2
}
Example Response
{
  "id": 45,
  "date": "2024-02-09",
  "location": "North Beach, FL",
  "lat": 25.860567,
  "lng": -80.123494,
  "image": "https://images.unsplash.com/photo-1542252223-c7f5b1142f93",
  "user": {
    "id": 1,
    "email": "user1@example.com",
    "first_name": "Emily",
    "last_name": "Johnson",
    "location": "Miami, FL",
    "lat": 25.7616798,
    "lng": -80.1917902
  },
  "bird": {
    "id": 2,
    "common_name": "Snow Goose",
    "sci_name": "Anser caerulescens",
    "conservation_status": "Low Concern",
    "image": "https://images.unsplash.com/photo-1542252223-c7f5b1142f93",
    "sightings": null
  }
}
List Sightings By User:

To receive only Sightings of a specific bird, supply common_name. Otherwise, all Sightings for User are returned.

Example Request:
URL: /api/users/1/sightings/?common_name=Snow%20Goose
Method: GET
Example Response
[
  {
    "id": 40,
    "date": "2024-02-03",
    "location": "South Beach, FL",
    "lat": 25.7826123,
    "lng": -80.1340772,
    "image": "https://images.unsplash.com/photo-1542252223-c7f5b1142f93",
    "user": {
      "id": 1,
      "email": "user1@example.com",
      "first_name": "Emily",
      "last_name": "Johnson",
      "location": "Miami, FL",
      "lat": 25.7616798,
      "lng": -80.1917902
    },
    "bird": {
      "id": 2,
      "common_name": "Snow Goose",
      "sci_name": "Anser caerulescens",
      "conservation_status": "Low Concern",
      "image": "https://images.unsplash.com/photo-1542252223-c7f5b1142f93",
      "sightings": null
    }
  }
]
Get Sighting Details:
Example Request:
URL: /api/users/1/sightings/40
Method: GET
Example Response
[
  {
    "id": 40,
    "date": "2024-02-03",
    "location": "South Beach, FL",
    "lat": 25.7826123,
    "lng": -80.1340772,
    "image": "https://images.unsplash.com/photo-1542252223-c7f5b1142f93",
    "user": {
      "id": 1,
      "email": "user1@example.com",
      "first_name": "Emily",
      "last_name": "Johnson",
      "location": "Miami, FL",
      "lat": 25.7616798,
      "lng": -80.1917902
    },
    "bird": {
      "id": 2,
      "common_name": "Snow Goose",
      "sci_name": "Anser caerulescens",
      "conservation_status": "Low Concern",
      "image": "https://images.unsplash.com/photo-1542252223-c7f5b1142f93",
      "sightings": null
    }
  }
]
Update sighting:
Example Request
URL: /api/users/1/sightings/40
Method: PUT
{
  "date": "2024-02-03",
  "location": "North Beach, FL",
  "image": "https://images.unsplash.com/photo-1542252223-c7f5b1142f93",
  "bird_id": 2
}
Example Response
{
  "id": 40,
  "date": "2024-02-03",
  "location": "North Beach, FL",
  "lat": 25.860567,
  "lng": -80.123494,
  "image": "https://images.unsplash.com/photo-1542252223-c7f5b1142f93",
  "user": {
    "id": 1,
    "email": "user1@example.com",
    "first_name": "Emily",
    "last_name": "Johnson",
    "location": "Miami, FL",
    "lat": 25.7616798,
    "lng": -80.1917902
  },
  "bird": {
    "id": 2,
    "common_name": "Snow Goose",
    "sci_name": "Anser caerulescens",
    "conservation_status": "Low Concern",
    "image": "https://images.unsplash.com/photo-1542252223-c7f5b1142f93",
    "sightings": null
  }
}
Delete sighting:
URL: /api/users/1/sightings/40
Method: DELETE
Example Response
true

Observations (eBirds)

Action Method URL
Get Obs US GET /ebirds/region
Get Obs Nearby GET /ebirds/nearby
Get Region Observations

Returns three recent observations from the eBirds API. Used by Carousel on the dashboard.

Example Request
URL: /ebirds/region
Method: GET
Example Response:
[
  {
    "common_name": "Red-tailed Hawk",
    "sci_name": "Buteo jamaicensis",
    "location": "NY-374, Plattsburgh US-NY 44.71616, -73.54012",
    "image": "https://images.unsplash.com/photo-1623974109467-68cd795737be"
  },
  {
    "common_name": "Horned Lark",
    "sci_name": "Eremophila alpestris",
    "location": "455 Donerville Rd, Lancaster US-PA (40.0190,-76.3953)",
    "image": "https://images.unsplash.com/photo-1631980851215-f200418ae825"
  },
  {
    "common_name": "Great Black-backed Gull",
    "sci_name": "Larus marinus",
    "location": "Gloucester Point Beach Park, Gloucester Point US-VA 37.24648, -76.50292",
    "image": "https://images.unsplash.com/photo-1656513321623-78d54f36dde8"
  }
]
Get Nearby Observations

Returns three recent eBirds observations within 50km radius of the logged in user.

Example Request
URL: /ebirds/nearby
Method: GET
Example Response:
[
  {
    "common_name": "Merlin",
    "sci_name": "Falco columbarius",
    "location": "West Matheson Hammock Park",
    "image": "https://images.unsplash.com/photo-1611656564077-d9acc625bfc5"
  },
  {
    "common_name": "Yellow-rumped Warbler",
    "sci_name": "Setophaga coronata",
    "location": "Dolphin Mall",
    "image": "https://images.unsplash.com/photo-1650818671693-4a71ddb9fda4"
  },
  {
    "common_name": "Brown Pelican",
    "sci_name": "Pelecanus occidentalis",
    "location": "Mondrian Hotel - South Beach",
    "image": "https://images.unsplash.com/photo-1517516745392-000dfd0d26c1"
  }
]

Third-Party API's 🦃

Top


eBirds

Used to get recent user-submitted bird sightings in the region or by User's latitude and longitude.

Recent observations in a region (eg. region_code: "US")

URL: https://api.ebird.org/v2/data/obs/{region_code}/recent/
Method: GET
Header: "X-eBirdApiToken": "{key}"
{
  "back": 14,               // 1-30; The number of days back to fetch observations.
  "maxResults": 10000,      // 1-10000; Only fetch this number of observations.
  "r": "any location code"  // default: (none); Fetch observations from up to 10 locations.
}

Recent nearby observations

URL: https://api.ebird.org/v2/data/obs/geo/recent/
Method: GET
{
  "back": 14,               // 1-30; The number of days back to fetch observations.
  "dist": 25,               // 0-50; The search radius from the given position, in kilometers.
  "lat": 37,                // -90 - 90; Required. Latitude to 2 decimal places.
  "lng": -97,               // -180 - 180; Required. Longitude to 2 decimal places.
  "maxResults": 10000,      // 1-10000; Only fetch this number of observations.
  "r": "any location code"  // default: (none); Fetch observations from up to 10 locations.
}
Example Result:
[
  {
    "speciesCode": "domgoo1",
    "comName": "Domestic goose sp. (Domestic type)",
    "sciName": "Anser sp. (Domestic type)",
    "locId": "L99381",
    "locName": "Stewart Park",
    "obsDt": "2017-08-23 08:14",
    "howMany": 2,
    "lat": 42.4613413,
    "lng": -76.5054578,
    "obsValid": true,
    "obsReviewed": false,
    "locationPrivate": false
  },
  {
    "speciesCode": "cangoo",
    "comName": "Canada Goose",
    "sciName": "Branta canadensis",
    "locId": "L1150539",
    "locName": "Hanshaw Rd. fields",
    "obsDt": "2017-08-23 20:05",
    "howMany": 30,
    "lat": 42.4663513,
    "lng": -76.4531064,
    "obsValid": true,
    "obsReviewed": false,
    "locationPrivate": false
  }
]

Nuthatch

Queried on a poller to build local Birds table. Used to provide bird information: an image url, scientific name, common name, and conservation status. The poller will only query for results where hasImg is true. pageSize is limited to 100.

The function get_birds(pageNum=1, pageSize=100) can be called from the module acl.nuthatch and takes optional arguments for page number, and page size. It returns a list of BirdIn objects.

The poller.py polls bird data from the nuthatch API once per day. It will automatically retrieve an initial instance of bird data by calling the get_bird_data function. The poller will poll a maximum of 4 pages of bird data (each consisting of 100 birds).

Action Method URL
List Birds GET https://nuthatch.lastelm.software/v2/birds
URL: https://nuthatch.lastelm.software/v2/birds/
Method: GET
Header:
  "accept": "application/json"
  "API-key": {key}
Example Query Parameters:
{
  "page": 1,
  "pageSize": 100,
  "hasImg": true,
  "operator": "AND"
}
Example Response:
{
  "entities": [
    {
      "images": [
        "https://images.unsplash.com/photo-1643650997626-0124dbb98261",
        "https://images.unsplash.com/photo-1644610901347-b05ec91bb9b2",
        "https://images.unsplash.com/photo-1641995171363-9bc67bfb1b7c"
      ],
      "lengthMin": "47",
      "lengthMax": "51",
      "name": "Black-bellied Whistling-Duck",
      "id": 1,
      "sciName": "Dendrocygna autumnalis",
      "region": ["North America"],
      "family": "Anatidae",
      "order": "Anseriformes",
      "status": "Low Concern"
    },
    {
      "images": [
        "https://images.unsplash.com/photo-1542252223-c7f5b1142f93",
        "https://images.unsplash.com/photo-1663238039107-87c35fbaee67"
      ],
      "lengthMin": "69",
      "lengthMax": "83",
      "name": "Snow Goose",
      "id": 4,
      "sciName": "Anser caerulescens",
      "region": ["North America"],
      "family": "Anatidae",
      "order": "Anseriformes",
      "status": "Low Concern"
    }
  ]
}

Google Geocoding

Action Method URL
Get Geocode GET https://maps.googleapis.com/maps/api/geocode/json
URL: https://maps.googleapis.com/maps/api/geocode/json/
Method: GET
Header:
  "address": string
  "api-key": {key}
Example Query Parameters:
{
  "address": "1600 Pennsylvania Ave, Washington DC"
}
Example Response:
{
  "results": [
    {
      "address_components": [
        {
          "long_name": "1600",
          "short_name": "1600",
          "types": [
            "street_number"
          ]
        },
        {
          "long_name": "Pennsylvania Avenue Northwest",
          "short_name": "Pennsylvania Avenue NW",
          "types": [
            "route"
          ]
        },
        {
          "long_name": "Northwest Washington",
          "short_name": "Northwest Washington",
          "types": [
            "neighborhood",
            "political"
          ]
        },
        {
          "long_name": "Washington",
          "short_name": "Washington",
          "types": [
            "locality",
            "political"
          ]
        },
        {
          "long_name": "District of Columbia",
          "short_name": "DC",
          "types": [
            "administrative_area_level_1",
            "political"
          ]
        },
        {
          "long_name": "United States",
          "short_name": "US",
          "types": [
            "country",
            "political"
          ]
        },
        {
          "long_name": "20500",
          "short_name": "20500",
          "types": [
            "postal_code"
          ]
        },
        {
          "long_name": "0005",
          "short_name": "0005",
          "types": [
            "postal_code_suffix"
          ]
        }
      ],
      "formatted_address": "1600 Pennsylvania Avenue NW, Washington, DC 20500, USA",
      "geometry": {
        "bounds": {
          "northeast": {
            "lat": 38.8979044,
            "lng": -77.0355124
          },
          "southwest": {
            "lat": 38.8973063,
            "lng": -77.03795749999999
          }
        },
        "location": {
          "lat": 38.8976633,
          "lng": -77.03657389999999
        },
        "location_type": "ROOFTOP",
        "viewport": {
          "northeast": {
            "lat": 38.8989543302915,
            "lng": -77.03538596970849
          },
          "southwest": {
            "lat": 38.8962563697085,
            "lng": -77.03808393029151
          }
        }
      },
      "place_id": "ChIJGVtI4by3t4kRr51d_Qm_x58",
      "types": [
        "establishment",
        "point_of_interest",
        "premise"
      ]
    }
  ],
  "status": "OK"
}

(Stretch Goal) Bird Classifier

Used to give bird classification prediction based on user uploaded photo to fascilitate bird selection process when adding a sighting.

URL: https://bird-classifier.p.rapidapi.com/BirdClassifier/prediction
Method: POST
Header:
  "Content-Type": "multipart/form-data",
  "x-rapidapi-host": "bird-classifier.p.rapidapi.com"
  "x-rapidapi-key": "{key}"
Example Query Parameters:
{
  "results": 5,
  "image": "Base64-encoded binary image data"
}
Example Response:
[
  {
    "scientificName": "Sialia sialis",
    "probability": 0.8156863
  },
  {
    "scientificName": "Sialia mexicana",
    "probability": 0.003921569
  },
  {
    "scientificName": "Haemorhous cassinii",
    "probability": 0
  },
  {
    "scientificName": "Aramus guarauna",
    "probability": 0
  },
  {
    "scientificName": "Rupornis magnirostris",
    "probability": 0
  }
]


About

Group Project from Hack Reactor Bootcamp

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Contributors 7