Milestone 2 Report - bounswe/bounswe2023group2 GitHub Wiki

CMPE352 Group 2 - Practice App

We designed this practice app to learn working as a group on github for future releases.

Contributors

Aras
Güngöre
Begüm
Arslan
Cahid Enes
Keleş
Egecan
Serbester
Halil İbrahim
Gürbüz
Hasan
Bingölbali
Mehmet Emin
İpekdal
Mehmet
Kuzulugil
Merve Gürbüz
Ömer Şahin Albayram
Ramazan Burak Sarıtaş

Table of Contents

1. Executive Summary

We used FastAPI for the backend and next.js for the front end for our practice app. The API's are picked considering future use. We succesfully deployed our docker on AWS.

  • URL of practice app code and the tag https://practice-app.online/notifications : Appended late by instructor's permission due to https.
  • Any information we may need to test your application Check the README for server and client
  • Instructions for building the application with docker. – API URL (the URL should take us to a documented API) https://practice-app.online/api/docs
  • Links to meeting notes related to practice app
  • Lessons learned: evaluation of tools and processes utilized to manage the team project.
    • Visual Studio Code
    • Github
    • FastAPI The easiness with documenting API's were notable.
    • MongoDB and PostgreSQL This was a challenge we faced as a group. We decided on using both after careful consideration to test them for our purposes. ``

2. Member Contributions

2.1. Aras Güngöre

  • Links to important issues

    • Issues related to research and documentation: #162
    • Issues related to Practice App: #175 #194 #227
  • The name, route, description of utilized third party URIs

    • TimeZoneDB API
    • endpoint: http://api.timezonedb.com/v2.1/get-time-zone?key=API_KEY
    • POST request
      • Description: Get local time of a city by its name, time zone, latitude & longtiude, or IP address.
        • Parameters:
          • key: Your unique API key you get after register your account.
          • by: The method of lookup
          • zone: A time zone abbreviation or time zone name. Required if lookup by zone method.
          • lat: Latitude of a city. Required if lookup by position method.
          • lng: Longitude of a city. Required if lookup by position method.
          • city: The name of a city. Use asterisk (*) for wildcard search. Required if lookup by city method.
  • The name, route, description of the created API functions

    • get_timezone
      • Route: /timezone/get_timezone
      • Description: Convert latitude and longitude data to timezone and date.
    • list_timezones
      • Route: /tz_conversion/list_timezones
      • Description: Retrieve a list of all available timezones from the Timezone DB API.
    • convert_time
      • Route: /tz_conversion/convert_time
      • Description: Convert a given time from one timezone to another.
    • saved_conversions
      • Route: /tz_conversion/saved_conversions
      • Description: Retrieve a list of saved time conversions from the MongoDB database.
    • delete_conversion
      • Route: /tz_conversion/delete_conversion
      • Delete a saved time conversion from the MongoDB database.
  • Description of unit tests. You can include code fragments to illustrate.

I have written 5 unit tests, one for each API function.

class TimezoneTests(unittest.TestCase):
    def test_get_timezone(self):
        r = requests.post(f'http://0.0.0.0:8000{api_prefix}/timezone/get_timezone', json={
            "latitude": "122",
            "longitude": "232",
        })
        self.assertEqual(r.status_code, 200)

    @pytest.mark.parametrize(
        "expected_status_code",
        [(200)],
    )
    def test_list_timezones(expected_status_code):
        client = TestClient(app)
        response = client.get("/tz_conversion/list_timezones")
        assert response.status_code == expected_status_code


    @pytest.mark.parametrize(
        "time, from_tz, to_tz, expected_status_code",
        [
            ("2023-05-12 15:00:00", "America/New_York", "Europe/London", 200),
            ("2023-05-12 15:00:00", "Invalid timezone", "Europe/London", 400),
            ("2023-05-12 15:00:00", "America/New_York", "Invalid timezone", 400),
            ("2023-05-12T15:00:00", "America/New_York", "Europe/London", 400),
        ],
    )
    def test_convert_time(time, from_tz, to_tz, expected_status_code):
        client = TestClient(app)
        response = client.post(
            "/tz_conversion/convert_time", json={"time": time, "from_tz": from_tz, "to_tz": to_tz}
        )
        assert response.status_code == expected_status_code


    @pytest.mark.parametrize(
        "expected_status_code",
        [(200)],
    )
    def test_get_saved_conversions(expected_status_code):
        client = TestClient(app)
        response = client.get("/tz_conversion/saved_conversions")
        assert response.status_code == expected_status_code


    @pytest.mark.parametrize(
        "conversion_name, expected_status_code",
        [
            ("Invalid name", 404),
        ],
    )
    def test_delete_conversion(conversion_name, expected_status_code):
        client = TestClient(app)
        response = client.post("/tz_conversion/delete_conversion",
                            json={"conversion_name": conversion_name})
        assert response.status_code == expected_status_code
  • Sample calls

import requests

uri = 'http://127.0.0.1:8000'

# POST Request Example
response = requests.post(uri + '/timezone/get_timezone', json={
    "latitude": 120.1,
    "longitude": 23.4
})

print('Headers:', response.headers)
print('Status Code:', response.status_code)
print('Json:', response.json())
Headers: {'date': 'Fri, 12 May 2023 20:37:25 GMT', 'server': 'uvicorn', 'content-length': '61', 'content-type': 'application/json'}
Status Code: 200
Json: {'timezone': 'America/Anchorage', 'date': '2023-05-12 12:37:23'}
# GET Request Example
response = requests.get(uri + '/tz_conversion/list_timezones')

print('Headers:', response.headers)
print('Status Code:', response.status_code)
print('Json:', response.json())
Headers: {'date': 'Fri, 12 May 2023 20:37:25 GMT', 'server': 'uvicorn', 'content-length': '7722', 'content-type': 'application/json'}
Status Code: 200
Json: ['Africa/Abidjan', 'Africa/Accra', 'Africa/Addis_Ababa', 'Africa/Algiers', 'Africa/Asmara', 'Africa/Bamako', 'Africa/Bangui', 'Africa/Banjul', 'Africa/Bissau', 'Africa/Blantyre', 'Africa/Brazzaville', 'Africa/Bujumbura', 'Africa/Cairo', 'Africa/Casablanca', 'Africa/Ceuta', 'Africa/Conakry', 'Africa/Dakar', 'Africa/Dar_es_Salaam', 'Africa/Djibouti', 'Africa/Douala', 'Africa/El_Aaiun', 'Africa/Freetown', 'Africa/Gaborone', 'Africa/Harare', 'Africa/Johannesburg', 'Africa/Juba', 'Africa/Kampala', 'Africa/Khartoum', 'Africa/Kigali', 'Africa/Kinshasa', 'Africa/Lagos', 'Africa/Libreville', 'Africa/Lome', 'Africa/Luanda', 'Africa/Lubumbashi', 'Africa/Lusaka', 'Africa/Malabo', 'Africa/Maputo', 'Africa/Maseru', 'Africa/Mbabane', 'Africa/Mogadishu', 'Africa/Monrovia', 'Africa/Nairobi', 'Africa/Ndjamena', 'Africa/Niamey', 'Africa/Nouakchott', 'Africa/Ouagadougou', 'Africa/Porto-Novo', 'Africa/Sao_Tome', 'Africa/Tripoli', 'Africa/Tunis', 'Africa/Windhoek', 'America/Adak', 'America/Anchorage', 'America/Anguilla', 'America/Antigua', 'America/Araguaina', 'America/Argentina/Buenos_Aires', 'America/Argentina/Catamarca', 'America/Argentina/Cordoba', 'America/Argentina/Jujuy', 'America/Argentina/La_Rioja', 'America/Argentina/Mendoza', 'America/Argentina/Rio_Gallegos', 'America/Argentina/Salta', 'America/Argentina/San_Juan', 'America/Argentina/San_Luis', 'America/Argentina/Tucuman', 'America/Argentina/Ushuaia', 'America/Aruba', 'America/Asuncion', 'America/Atikokan', 'America/Bahia', 'America/Bahia_Banderas', 'America/Barbados', 'America/Belem', 'America/Belize', 'America/Blanc-Sablon', 'America/Boa_Vista', 'America/Bogota', 'America/Boise', 'America/Cambridge_Bay', 'America/Campo_Grande', 'America/Cancun', 'America/Caracas', 'America/Cayenne', 'America/Cayman', 'America/Chicago', 'America/Chihuahua', 'America/Ciudad_Juarez', 'America/Costa_Rica', 'America/Creston', 'America/Cuiaba', 'America/Curacao', 'America/Danmarkshavn', 'America/Dawson', 'America/Dawson_Creek', 'America/Denver', 'America/Detroit', 'America/Dominica', 'America/Edmonton', 'America/Eirunepe', 'America/El_Salvador', 'America/Fort_Nelson', 'America/Fortaleza', 'America/Glace_Bay', 'America/Goose_Bay', 'America/Grand_Turk', 'America/Grenada', 'America/Guadeloupe', 'America/Guatemala', 'America/Guayaquil', 'America/Guyana', 'America/Halifax', 'America/Havana', 'America/Hermosillo', 'America/Indiana/Indianapolis', 'America/Indiana/Knox', 'America/Indiana/Marengo', 'America/Indiana/Petersburg', 'America/Indiana/Tell_City', 'America/Indiana/Vevay', 'America/Indiana/Vincennes', 'America/Indiana/Winamac', 'America/Inuvik', 'America/Iqaluit', 'America/Jamaica', 'America/Juneau', 'America/Kentucky/Louisville', 'America/Kentucky/Monticello', 'America/Kralendijk', 'America/La_Paz', 'America/Lima', 'America/Los_Angeles', 'America/Lower_Princes', 'America/Maceio', 'America/Managua', 'America/Manaus', 'America/Marigot', 'America/Martinique', 'America/Matamoros', 'America/Mazatlan', 'America/Menominee', 'America/Merida', 'America/Metlakatla', 'America/Mexico_City', 'America/Miquelon', 'America/Moncton', 'America/Monterrey', 'America/Montevideo', 'America/Montserrat', 'America/Nassau', 'America/New_York', 'America/Nome', 'America/Noronha', 'America/North_Dakota/Beulah', 'America/North_Dakota/Center', 'America/North_Dakota/New_Salem', 'America/Nuuk', 'America/Ojinaga', 'America/Panama', 'America/Paramaribo', 'America/Phoenix', 'America/Port-au-Prince', 'America/Port_of_Spain', 'America/Porto_Velho', 'America/Puerto_Rico', 'America/Punta_Arenas', 'America/Rankin_Inlet', 'America/Recife', 'America/Regina', 'America/Resolute', 'America/Rio_Branco', 'America/Santarem', 'America/Santiago', 'America/Santo_Domingo', 'America/Sao_Paulo', 'America/Scoresbysund', 'America/Sitka', 'America/St_Barthelemy', 'America/St_Johns', 'America/St_Kitts', 'America/St_Lucia', 'America/St_Thomas', 'America/St_Vincent', 'America/Swift_Current', 'America/Tegucigalpa', 'America/Thule', 'America/Tijuana', 'America/Toronto', 'America/Tortola', 'America/Vancouver', 'America/Whitehorse', 'America/Winnipeg', 'America/Yakutat', 'Antarctica/Casey', 'Antarctica/Davis', 'Antarctica/DumontDUrville', 'Antarctica/Macquarie', 'Antarctica/Mawson', 'Antarctica/McMurdo', 'Antarctica/Palmer', 'Antarctica/Rothera', 'Antarctica/Syowa', 'Antarctica/Troll', 'Antarctica/Vostok', 'Arctic/Longyearbyen', 'Asia/Aden', 'Asia/Almaty', 'Asia/Amman', 'Asia/Anadyr', 'Asia/Aqtau', 'Asia/Aqtobe', 'Asia/Ashgabat', 'Asia/Atyrau', 'Asia/Baghdad', 'Asia/Bahrain', 'Asia/Baku', 'Asia/Bangkok', 'Asia/Barnaul', 'Asia/Beirut', 'Asia/Bishkek', 'Asia/Brunei', 'Asia/Chita', 'Asia/Choibalsan', 'Asia/Colombo', 'Asia/Damascus', 'Asia/Dhaka', 'Asia/Dili', 'Asia/Dubai', 'Asia/Dushanbe', 'Asia/Famagusta', 'Asia/Gaza', 'Asia/Hebron', 'Asia/Ho_Chi_Minh', 'Asia/Hong_Kong', 'Asia/Hovd', 'Asia/Irkutsk', 'Asia/Jakarta', 'Asia/Jayapura', 'Asia/Jerusalem', 'Asia/Kabul', 'Asia/Kamchatka', 'Asia/Karachi', 'Asia/Kathmandu', 'Asia/Khandyga', 'Asia/Kolkata', 'Asia/Krasnoyarsk', 'Asia/Kuala_Lumpur', 'Asia/Kuching', 'Asia/Kuwait', 'Asia/Macau', 'Asia/Magadan', 'Asia/Makassar', 'Asia/Manila', 'Asia/Muscat', 'Asia/Nicosia', 'Asia/Novokuznetsk', 'Asia/Novosibirsk', 'Asia/Omsk', 'Asia/Oral', 'Asia/Phnom_Penh', 'Asia/Pontianak', 'Asia/Pyongyang', 'Asia/Qatar', 'Asia/Qostanay', 'Asia/Qyzylorda', 'Asia/Riyadh', 'Asia/Sakhalin', 'Asia/Samarkand', 'Asia/Seoul', 'Asia/Shanghai', 'Asia/Singapore', 'Asia/Srednekolymsk', 'Asia/Taipei', 'Asia/Tashkent', 'Asia/Tbilisi', 'Asia/Tehran', 'Asia/Thimphu', 'Asia/Tokyo', 'Asia/Tomsk', 'Asia/Ulaanbaatar', 'Asia/Urumqi', 'Asia/Ust-Nera', 'Asia/Vientiane', 'Asia/Vladivostok', 'Asia/Yakutsk', 'Asia/Yangon', 'Asia/Yekaterinburg', 'Asia/Yerevan', 'Atlantic/Azores', 'Atlantic/Bermuda', 'Atlantic/Canary', 'Atlantic/Cape_Verde', 'Atlantic/Faroe', 'Atlantic/Madeira', 'Atlantic/Reykjavik', 'Atlantic/South_Georgia', 'Atlantic/St_Helena', 'Atlantic/Stanley', 'Australia/Adelaide', 'Australia/Brisbane', 'Australia/Broken_Hill', 'Australia/Darwin', 'Australia/Eucla', 'Australia/Hobart', 'Australia/Lindeman', 'Australia/Lord_Howe', 'Australia/Melbourne', 'Australia/Perth', 'Australia/Sydney', 'Europe/Amsterdam', 'Europe/Andorra', 'Europe/Astrakhan', 'Europe/Athens', 'Europe/Belgrade', 'Europe/Berlin', 'Europe/Bratislava', 'Europe/Brussels', 'Europe/Bucharest', 'Europe/Budapest', 'Europe/Busingen', 'Europe/Chisinau', 'Europe/Copenhagen', 'Europe/Dublin', 'Europe/Gibraltar', 'Europe/Guernsey', 'Europe/Helsinki', 'Europe/Isle_of_Man', 'Europe/Istanbul', 'Europe/Jersey', 'Europe/Kaliningrad', 'Europe/Kirov', 'Europe/Kyiv', 'Europe/Lisbon', 'Europe/Ljubljana', 'Europe/London', 'Europe/Luxembourg', 'Europe/Madrid', 'Europe/Malta', 'Europe/Mariehamn', 'Europe/Minsk', 'Europe/Monaco', 'Europe/Moscow', 'Europe/Oslo', 'Europe/Paris', 'Europe/Podgorica', 'Europe/Prague', 'Europe/Riga', 'Europe/Rome', 'Europe/Samara', 'Europe/San_Marino', 'Europe/Sarajevo', 'Europe/Saratov', 'Europe/Simferopol', 'Europe/Skopje', 'Europe/Sofia', 'Europe/Stockholm', 'Europe/Tallinn', 'Europe/Tirane', 'Europe/Ulyanovsk', 'Europe/Vaduz', 'Europe/Vatican', 'Europe/Vienna', 'Europe/Vilnius', 'Europe/Volgograd', 'Europe/Warsaw', 'Europe/Zagreb', 'Europe/Zurich', 'Indian/Antananarivo', 'Indian/Chagos', 'Indian/Christmas', 'Indian/Cocos', 'Indian/Comoro', 'Indian/Kerguelen', 'Indian/Mahe', 'Indian/Maldives', 'Indian/Mauritius', 'Indian/Mayotte', 'Indian/Reunion', 'Pacific/Apia', 'Pacific/Auckland', 'Pacific/Bougainville', 'Pacific/Chatham', 'Pacific/Chuuk', 'Pacific/Easter', 'Pacific/Efate', 'Pacific/Fakaofo', 'Pacific/Fiji', 'Pacific/Funafuti', 'Pacific/Galapagos', 'Pacific/Gambier', 'Pacific/Guadalcanal', 'Pacific/Guam', 'Pacific/Honolulu', 'Pacific/Kanton', 'Pacific/Kiritimati', 'Pacific/Kosrae', 'Pacific/Kwajalein', 'Pacific/Majuro', 'Pacific/Marquesas', 'Pacific/Midway', 'Pacific/Nauru', 'Pacific/Niue', 'Pacific/Norfolk', 'Pacific/Noumea', 'Pacific/Pago_Pago', 'Pacific/Palau', 'Pacific/Pitcairn', 'Pacific/Pohnpei', 'Pacific/Port_Moresby', 'Pacific/Rarotonga', 'Pacific/Saipan', 'Pacific/Tahiti', 'Pacific/Tarawa', 'Pacific/Tongatapu', 'Pacific/Wake', 'Pacific/Wallis']
  • Any other significant work related to this release

Screenshot_1 I have developed a web app so when I enter a location in latitude and longitude, it displays the current time and time zone along with its respective location on the map.

  • Challenges

    • I initially wanted to use Google Maps Directions API for getting directions to a disaster area, however it was too difficult to implement so I switched to a more simple TimeZoneDB API.
    • Difference in payload between client-side and server-side caused Unprocessable Entity problem which took me a while to find the source of it.

2.2. Begüm Arslan

https://practice-app.online/notifications

  • Links to important issues

    Issues related to research and documentation: #164 #162 #160 #199 #195 related to practice App: #213 #197 #190 #187 #181 #171

  • The name, route, description of utilized third party URIs

    • Instance ID API
    • endpoint: https://iid.googleapis.com/iid/info/IID_TOKEN
    • GET request
      • Description:
        • Authorization: key=YOUR_API_KEY. Set this parameter in the header. *[optional] boolean details: set this query parameter to true to get FCM or GCM topic subscription information (if any) associated with this token. When not specified, defaults to false. On success the call returns HTTP status 200 and a JSON object containing:
        • application - package name associated with the token.
        • authorizedEntity - projectId authorized to send to the token.
        • applicationVersion - version of the application.
        • appSigner - sha1 fingerprint for the signature applied to the package.
        • Indicates which party signed the app; for example,Play Store.
        • platform - returns ANDROID, IOS, or CHROME to indicate the device platform to which the token belongs. If the details flag is set:
        • rel - relations associated with the token. For example, a list of topic subscriptions. I used the API for retriveving topics subscribed for client tokens to show it on notifications page.
    • endpoint: https://iid.googleapis.com/iid/v1/IID_TOKEN/rel/topics/TOPIC_NAME
    • POST request
      • Works similarly for subscribing a topic. I used the functions for the usage of this API: messaging.subscribe_to_topic(subscription.token, subscription.topic)
    • Firebase Cloud Messaging API
    • Endpoint https://fcm.googleapis.com/v1/{parent=projects/*}/messages:send
    • POST request
      • parent: It contains the Firebase project id
      • data has the message with topic, title and body and headers include an authorization token.
      • The api can be used for sending notifications to topic and a client token
  • The name, route, description of the created API functions

    • create_subscription
      • Route: /notifications/subscriptions
      • Description: This function uses firebase API to subscribe a client to a topic using client token. Also the subscription is added to database.
    • unsubscription
      • Route: /notifications/unsubscribe
      • Description: This function uses firebase API to unsubscribe a client from a topic using client token. Also the subscription is added to database.
    • get_subscriptions
      • Route: /notifications/subscriptions
      • Description: This function return a list of subscriptions from database.
    • get_client_info
      • Route: /notifications/subscriptions/{device_token}
      • Description: This function return a list of subscriptions of a single client using Instance ID API using device token.
    • send_notif
      • Route: /notifications/send_notification_to_one
      • Description: This function sends a single notification to a device token.
    • create_event
      • Route: /notifications/send_notification
      • Description: This function sends notifications to devices that are subscribed to a topic.
    • get_notif
      • Route: /notifications/send_notification
      • Description: This function gets notifications that were sent.
  • Description of unit tests. You can include code fragments to illustrate.

I have written 8 tests for the functions that checks if the calls are successfull: image

  • Sample calls

    • Request:
       axios.post(`${process.env.NEXT_PUBLIC_BACKEND_URL}/notifications/send_notification`, JSON.stringify(notificationData), {
          headers: {
            'content-type': 'application/json'
          }
        });
      
    • Response:
      HTTP/1.1 200 OK
      content-type: application/json
      [
          {notification: 'Driver Need in Istanbul', message: 'Notification is sended'}
      ]
    • Request:
       axios.post(`${process.env.NEXT_PUBLIC_BACKEND_URL}/notifications/subscriptions`, JSON.stringify(subscriptionData), {
        headers: {
          'content-type': 'application/json'
        }
        });
      
    • Response:
      HTTP/1.1 200 OK
      content-type: application/json
      [
        {success_count: 1, message: 'Tokens were subscribed successfully'}
       ]
    • Request:
       axios.post(`${process.env.NEXT_PUBLIC_BACKEND_URL}/notifications/unsubscribe`, JSON.stringify(subscriptionData), {
      
          headers: {
            'content-type': 'application/json'
          }
        });
      
    • Response:
      HTTP/1.1 200 OK
      content-type: application/json
      [ 
        {success_count: 1, message: 'Tokens were unsubscribed successfully'}
      ]  
    • Request:
      axios.get(`${process.env.NEXT_PUBLIC_BACKEND_URL}/notifications/subscriptions/${token}`, {
      headers: {
        'content-type': 'application/json'
      }
      });
      
    • Response:
      HTTP/1.1 200 OK
      content-type: application/json
      ['Driver', 'Food']
  • Challenges

I have had a hard time with Firebase Cloud Messaging API configuration. I had to find a way to get Bearer token that expires in a short time. I used many secret keys and tokens, which caused some trouble to my group members:) Also I never had an experience with APIs, FastAPI and next.js. For all of those things I got many help from Merve Gürbüz. Also Firebase needs HTTPS connection for sending notification and others, I used ngrok for test purposes. I also had a hard time with git, but I feel more confident at resolving merge conflicts now. The most stressfull part was about database choice, it was decided to use NoSQL on the meeting but later on we decided on using both because it was asked. But on the implementation side it caused problems. I think it should be a good lesson that we should always follow the meetings, And if you are not confident about something and wont take responsibility, please trust other's opinion.

  • Any other significant work related to this release

I tried to find APIs and resources for others to learn. Mainly focused on my individual work due to lack of knowledge.

2.3 Cahid Enes Keleş

Links to Important Issues and Wikis

The Name, Route, Description of Utilized Third Party URI

The Name, Route, and Description of The Created API Functions

I used /addel for all my endpoints. Following routes adds to /addel

  • /add/resource: POST with json body: Add resource to the database
  • /add/need: POST with json body: Add need to the database
  • /add/event: POST with json body: Add event to the database
  • /add/action: POST with json body: Add action to the database
  • /get/resource: GET: get the list of resources
  • /get/need: GET: get the list of needs
  • /get/event: GET: get the list of events
  • /get/action: GET: get the list of actions
  • /get/resource?id=<id>: GET: Get resource with id
  • /get/need?id=<id>: GET: Get need with id
  • /get/event?id=<id>: GET: Get event with id
  • /get/action?id=<id>: GET: Get action with id
  • /delete/resource?id=<id>: GET: Delete resource with id
  • /delete/need?id=<id>: GET: Delete need with id
  • /delete/event?id=<id>: GET: Delete event with id
  • /delete/action?id=<id>: GET: Delete action with id

Note: The parameters of the POST requests can be seen and be tested in docs: Swagger or Redoc. Note that these documentations also include UI endpoints as well.

Description of Unit Tests

For each combination of (resource, need, event, action) and (add, get, delete) there are related tests, there is no API endpoint that have not any unit test. There are total of 70 unit tests (and 1 mock test to cleanup).

For add tests, there are

  • Full arguments tests
  • Only required arguments tests
  • 1 required argument missing tests for each required argument (should fail)

For get tests, there are

  • Empty list tests
  • At least 3 tests
  • ID tests
  • Unpresent ID tests (should return null)

For delete tests, there are

  • ID tests
  • Unpresent ID tests

An example test is as follows:

    def test_full_arguments(self):
        r = requests.post(f'http://0.0.0.0:8000{api_prefix}/add/resource', json={
            "type": "food",
            "location": "1234",
            "notes": "this resource is here for a test",
            "updated_at": "2020-04-01T12:00:00Z",
            "is_active": True,
            "upvotes": 0,
            "downvotes": 0,
            "creator_id": "test",
            "creation_date": "2020-04-01T12:00:00Z",
            "condition": "good",
            "quantity": 1
        })
        # test response uuid
        self.assertEqual(r.status_code, 200)
        uuid = r.text[1:-1]
        self.assertTrue(uuid4regex.match(uuid), f'uuid {uuid} does not match regex {uuid4regex.pattern}')
        return uuid

Sample Calls

Two types of requests have two different use of calls. POST requests require a json body, while (some of) GET requests require inline parameters. For each type of requests, the example is as follows:

import requests

uri = 'http://13.49.41.10:8000'

# POST Request Example
response = requests.post(uri + '/add/resource', json={
    "type": "Food",
    "location": "Istanbul",
    "notes": "This is a test resource",
    "updated_at": "12.05.2023",
    "is_active": True,
    "upvotes": 0,
    "downvotes": 0,
    "creator_id": "293874",
    "creation_date": "12.05.2023",
    "condition": "New",
    "quantity": 1
})

print('Headers:', response.headers)
print('Status Code:', response.status_code)
print('Json:', response.json())
print()

# GET Request Example
response = requests.get(uri + '/get/resource?id=' + response.json())
print('Headers:', response.headers)
print('Status Code:', response.status_code)
print('Json:', response.json())

And the output is:

Headers: {'date': 'Fri, 12 May 2023 20:17:48 GMT', 'server': 'uvicorn', 'content-length': '38', 'content-type': 'application/json'}
Status Code: 200
Json: 787d8f8a-c9df-4fcc-b5fa-5c8d860dc630

Headers: {'date': 'Fri, 12 May 2023 20:17:48 GMT', 'server': 'uvicorn', 'content-length': '268', 'content-type': 'application/json'}
Status Code: 200
Json: {'uuid': '787d8f8a-c9df-4fcc-b5fa-5c8d860dc630', 'type': 'Food', 'location': 'Istanbul', 'notes': 'This is a test resource', 'updated_at': '12.05.2023', 'is_active': True, 'upvotes': 0, 'downvotes': 0, 'creator_id': '293874', 'creation_date': '12.05.2023', 'condition': 'New', 'quantity': 1}

Any Other Significant Work Related to This Release

I initially thought that we should make the dockerization and deployment by ourselves, and I did all these things. Here is the API that I deployed myself: http://13.49.41.10:8000/ (later I merged my code with shared code and docker and amazon server) The Dockerfile and other files related to the dockerization and deployment are deleted. But you can see these files in the history. As backend, I (and we) used fastapi. As database, I used PostgreSQL.

Challanges I Met

There were a lot of new things to learn in a short time, that was very very challenging. I initially tried to use MongoDB for database. I worked very hard and melted down 5 hours just struggling and I was failed. This was very depressing for me. But I recovered tomorrow, and used PostgreSQL (with the suggestions of my teammates: it is better if we could use the advantages of different types of databases).

2.4 Egecan Serbester

Links to Important Issues

The name, route, and description of utilized third-party URIs (sample calls are included)

Twilio API

I post Twilio API twice once for sending SMS to the proper phone number. I checked them if it contains +90 for Turkey and has a length of 11 like 0532 123 45 67.

Here is the sendMessage method's code where I sent a POST method to Twilio.

image

Here is the result of calling this function:

image

However, because sending an SMS needs verified outgoing taller numbers I also post another method that adds the number to verified outgoing caller numbers. I also wrote a function to handle this. Here is the code for that:

Screenshot 2023-05-29 at 06 32 38

However, because my Twilio account is on trial I couldn't post for them. Here is the response from Twilio:

image

The name, route, and description of the created API functions (sample calls are included)

On the register page I post to my created API exist, pw_not_ok, phone_nut_turkey, and registered attributes in JSON format.

In the 'exist' object I checked if the username already exists in the database, if it returns true user can not register the system because usernames must be unique.

image

In the 'pw_not_ok', I checked if password credentials contain at least 8 characters, if it doesn't contain at least 8 characters user couldn't register.

image

In the 'phone_nut_turkey', I checked if the phone number isn't suitable for Turkey, if it doesn't start with 05 and doesn't contain 11 characters user couldn't register to the system.

image

If a user can successfully register to the system, it first saved its credentials to the Mongo database. After that, it calls a send message method which I connected to Twilio API and sent the message and the number to be received as an SMS which I explained above.

Here is the code for my register page, where the router is APIRouter() of FastAPI which is a created API.

image

In the login page, I use the post method to check user credentials from the database, if they are okay user can go dummy home page by clicking login. Here is the code:

image

Here is the example when the credentials are not OK for login:

image

In the dummy home page, I used the get method with the query parameter of the username that is logged in, it goes to an endpoint with the username.

Here is the result:

image

Challenges

Four of us (team members) were together for 2 days to contribute to this project. On the deadline night, coffee was spilled into my computer by mistake. I couldn't write the report smoothly that night because I had no time. My plan was to write this report the morning after that night. However, my computer couldn't run the morning after that night, because my mainboard was oxidized. Therefore I couldn't write this report on time. I'm very sorry for this.

Another challenge for us (especially Merve): We were using MongoDB which we have already decided on 3 weeks ago, however some of our friends used Postgre. Therefore we got an error too much. They said they would handle those errors although, they couldn't. After they couldn't handle errors especially Merve tried so hard to fix them in the last hours of subjection.

2.5. Halil İbrahim Gürbüz

  • Links to important issues

  • The name, route, description of utilized third party URIs

    • Name: SendGrid API
    • Route: https://api.sendgrid.com/v3/mail/send
    • Description: SendGrid's web API allows our customers to pull information about their email program without having to actually log on to SendGrid.com.
  • The name, route, description of the created API functions

    Name: emailreport / POST

    • Route: /emailreport
    • Description: This function collects data from report form and sends an email about the report to the admin. In addition, it adds the report to the database.

    Name: add_admin / POST (*)

    • Route: /emailreport/add_admin
    • Description: This function adds an admin with given email address to the database. This email address is required to submit a report.

    Name: get_admin / GET (*)

    • Route: /emailreport/get_admin
    • Description: This function retrieve the admin's user information from the database. Reports are sent to this email address read from the database.

    Name: drop_admin_table / DELETE (*)

    • Route: /emailreport/drop_admin_table
    • Description: This function drops admin table from the database. This function is useful if accidentally or more than one admin has been added.
  • Description of unit tests. You can include code fragments to illustrate. (*)

def test_add_admin_success():
    email = "[email protected]"
    response = client.post('/emailreport/add_admin', json={"email": email})
    assert response.status_code == 200
    assert response.json() == {'message': 'Admin added successfully'}


def test_add_admin_failure():
    email = "[email protected]"
    response = client.post('/emailreport/add_admin', json={"email": email})
    assert response.status_code == 500


def test_get_admin_success():
    response = client.get('/emailreport/get_admin')
    assert response.status_code == 200
    expected_data = {
        "username": "admin4report",
        "first_name": "admin",
        "last_name": "istrator",
        "email": "[email protected]",
        "phone_number": "+905554443322"
    }
    assert response.json() == expected_data


def test_get_admin_failure():
    response = client.get('/emailreport/get_admin')
    assert response.status_code == 404
    expected_data = {
        "detail": "Admin not found in the DB"
    }
    assert response.json() == expected_data


def test_drop_admin_table_success():
    response = client.delete('/emailreport/drop_admin_table')
    assert response.status_code == 200
    expected_data = {
        "message": "Admin table dropped successfully"
    }
    assert response.json() == expected_data


def test_drop_admin_table_failure():
    response = client.delete('/emailreport/drop_admin_table')
    assert response.status_code == 500


def test_emailreport_success():
    report_data = {
        "reporter": "John Doe",
        "activity": "Activity 123",
        "reason": "Spam",
        "details": "This is a test report."
    }
    response = client.post('/emailreport', json=report_data)
    assert response.status_code == 200
    assert response.json()["reporter"] == "John Doe"
    assert response.json()["activity"] == "Activity 123"
    assert response.json()["reason"] == "Spam"
    assert response.json()["details"] == "This is a test report."


def test_emailreport_failure():
    report_data = {
        "reporter": "John Doe",
        "activity": "Activity 123",
        # Missing "reason" field
        "details": "This is a test report."
    }
    response = client.post('/emailreport', json=report_data)
    assert response.status_code == 422
  • Sample calls

- emailreport

# Request
curl -X 'POST' \
  'http://localhost:8000/emailreport/' \
  -H 'accept: application/json' \
  -H 'Content-Type: application/json' \
  -d '{
  "reporter": "John Doe",
  "activity": "NEED352",
  "reason": "Spam",
  "details": "Same need sent over and over"
}'

# Response
{
  "reporter": "John Doe",
  "activity": "NEED352",
  "reason": "Spam",
  "details": "Same need sent over and over"
}
- add_admin (*)

# Request
/emailreport/add_admin
curl -X 'POST' \
  'http://localhost:8000/emailreport/add_admin' \
  -H 'accept: application/json' \
  -H 'Content-Type: application/json' \
  -d '{
  "email": "[email protected]"
}'

# Response
{
  "message": "Admin added successfully"
}
- get_admin (*)

# Request
curl -X 'GET' \
  'http://localhost:8000/emailreport/get_admin' \
  -H 'accept: application/json'

# Response
{
  "username": "admin4report",
  "first_name": "admin",
  "last_name": "istrator",
  "email": "[email protected]",
  "phone_number": "+905554443322"
}
- drop_admin_table (*)

# Request
curl -X 'DELETE' \
  'http://localhost:8000/emailreport/drop_admin_table' \
  -H 'accept: application/json'

# Response
{
  "message": "Admin table dropped successfully"
}

email_report_ui_ss email_report_ss

  • Any other significant work related to this release

    • I tried to add the generated report to the database as well as sending an e-mail, finally solved the problem.
    • I added the parts indicated with (*) late with the permission of the instructer.
  • Challenges

    • I wasted a lot of time looking for an unused api suitable for the project. I tried Wikidata and GoogleMaps apis but I couldn't get the result I wanted.
    • Since I lost a lot of time in choosing the api, there is not enough time left for the backend and frontend part. Honestly, this part was more difficult than I thought. I had a hard time in the project as I have not used NextJS and MongoDb before.
    • My teammates, especially Egecan and Merve, helped me when I got stuck. Even though I couldn't finish it on time, I learned a lot of new things.

2.6. Hasan Bingölbali

  • Links to important issues

Implementing CI/CD

Deploying postgres

  • The name, route, description of utilized third party URIs

I implemented a tweet processor that recognizes address from a given sentence or paragraph Screenshot 2023-05-12 at 23 37 11

  • The name, route, description of the created API functions

http://ec2-13-49-231-144.eu-north-1.compute.amazonaws.com/word/word. This is the endpoint function I created. This endpoint functions calls hugging face endpoint deprem-ner endpoint

  • Description of unit tests. You can include code fragments to illustrate.

I implemented unit tests that one checks the behavior of my endpoint in case of missing required fields.

I implemented unit test that simulates the successful behavior.

I implemented unit test that returns error from server.

  • Sample calls

Screenshot 2023-05-12 at 23 46 20
  • Any other significant work related to this release

I really worked hard to install our fastapi server to aws. However, I also made the code modular for my teammates to work comfortably on their local pc. I also set-up ci/cd for immediate deployment and it worked without problem.

  • Challenges

My biggest challenge was the server crashes. Whenever you miss a requirement or change the credentials of db connection the system would not respond. We couldn't communicate well through the end of semester and lack of communication ended up with some argue. However our end server functions well and I learned a lot about docker. Especially to debug-logging and managing overall server and security management.

2.7. Mehmet Emin İpekdal

  • Links to important issues

  • The name, route, description of utilized third party URIs

  • The name, route, description of the created API functions

  • Description of unit tests. You can include code fragments to illustrate.

  • Sample calls

  • Any other significant work related to this release

  • Challenges

2.8. Mehmet Kuzulugil

  • Links to important issues

  • The name, route, description of utilized third party URIs

  • The name, route, description of the created API functions

    • Root for route is /on-twitter
    • Functions are:
      • /on-twitter/alive GET Shows that the API is alive and available
      • /on-twitter/check GET Checks for the database connection etc. Returns OK or Failed.
      • /on-twitter/getPublishedTweets/{eventId} GET Returns the urls of the published tweets about the event described by the eventId
      • /on-twitter/publishTweets/{eventId} POST Publish the event summary messages in a tweet chain.
  • Description of unit tests. You can include code fragments to illustrate.

    • Only a sample: The unit test to check the availability of the ssystem
    def test_alive(self):
          response = self.client.get("/on-twitter/alive")
          assert response.status_code == 200
          assert response.json() == {'Alive': 'Yes'}
  • Sample calls

http://localhost:8000/on-twitter/getPublishedTweets/100124

http://localhost:8000/on-twitter/publishTweets/100124

After two different calls to publishTweets API function: image

The twitter account is hidden for it will not be appropriate to publish tweets of disaster to public! One can follow though.

  • Any other significant work related to this release

    • Used a nosql database for the first time. It was a short experience, an introduction so to say.
    • Twitter API has its own problems and troubles. "Free" features are not well defined. My intentions to make use of it in smart ways failed. I had a plan to use the Twitter Spaces (audio chat with transcripting facilitise) as a means of communication with speech to text option. There is a python project for this by the way
    • For the UI staging of the API my choice was to learn and implemen next.js. It took time and was not so easy. Together with CMPE321 course project became a good opportunity though. Sample screen: image
  • Challenges

    • Twitter API itself was a challenge

2.9. Merve Gürbüz

  • Links to important issues

  • The name, route, description of utilized third party URIs

  • News API

    • endpoint: https://newsapi.org/v2/everything?
    • GET request
      • Description:
        • Authorization: key=YOUR_API_KEY. Set this parameter in the header.
        • Query Parameters: I have used q, sortBy and language query parameters and send them to my external API to retrieve data according to request This API help me to get hotline news from many sources by just one API.
  • The name, route, description of the created API functions

    • /news GET
      • Route: /news/?subject={}&language={}&sortBy={}
      • Description: This function fetches the data from newsAPI
    • /news/create POST
      • Route: /news/create
      • Description: This function inserts the data on MongoDB .
  • Description of unit tests. You can include code fragments to illustrate.

  • Sample calls

  • Any other significant work related to this release

    • I was experienced on frontend, backend and devops earlier, so I took responsibility on creating basic structure of practice and write readmes for both of them.I helped my team members as far as I can. I wrote the sample API endpoints for others to see an example. I implemented MONGODB connection and schemas under the folder database. Some people from team did not follow the decisions we made in meetings, and when we warn them about they just ignored it. It created many conflicts not just in the server but also in our own local environments. I had to write all dockerfiles and docker-compose file again. Begum's external API firebase doesnot accept http requests so I bought a domain and configure the DNS settings in Cloudflare. I dockerized NGINX as a proxy due to CORS problem.
  • Challenges

    • Finding an external API that is both free and related to our project was a bit challenging. I prioritize to write samples for my team mates so I generally did my own work late. We had decided to use Next.js and fastAPI for like a month ago but some decided to do whatever they like. So it took lots of my time to solve this problem. Moreover One of my teammate was so rude that he did not even accept the wrong that he made and continue doing that again again. He asked for everyone to change their APIs since he cannot help in deployment. So I had to start again and write the dockerfiles. I learnt how to use Cloudflare, NGINX and configure proxies.

2.10. Ömer Şahin Albayram

  • Links to important issues

    • #225, #188, #226

    • The name, route, description of the created API functions

      • get_coord
        • Route: /location
        • Description: This function collects coordinate values from MongoDB collection and responses it.
      • insert_coord
        • Route: /location/insert
        • Description: This function retrieves new markers and adds it to MongoDB.
    • Description of unit tests. You can include code fragments to illustrate.

      • Tests similar to the following were made during the implementation:
        • def test_locations_fetch():
          response = client.get("/location/")
          assert response.status_code == 200
          assert response.json() == [
             {'username': 'ali', 'x_coord': 37.08328368718349, 'y_coord': 36.83810055490806},
             {'username': 'kemal', 'x_coord': 38.934396015417725, 'y_coord': 38.32618261611021},
             {'username': 'ayşe', 'x_coord': 39.915131681651594, 'y_coord': 37.35510170315383}
              ]
          
          
          def test_locations_insert():
          response = client.post("/location/insert",json={"items": [{'x_coord': 38.64261790634527, 'y_coord': 37.27661132812501}]})
          assert response.status_code == 200
          assert response.json() == [
              {'x_coord': '38.64261790634527', 'y_coord': '37.27661132812501'}
           ]
    • Sample calls

      • Request:
         axios.get('http://127.0.0.1:8000/location/')
        
      • Response:
        HTTP/1.1 200 OK
        content-type: application/json
        [
        {'username': 'ali', 'x_coord': 37.08328368718349, 'y_coord': 36.83810055490806},
        {'username': 'kemal', 'x_coord': 38.934396015417725, 'y_coord': 38.32618261611021},
        {'username': 'ayşe', 'x_coord': 39.915131681651594, 'y_coord': 37.35510170315383}
        ]
        
      • Request:
         axios.post("/location/insert",json={"items": [{'x_coord': 38.64261790634527, 'y_coord': 37.27661132812501}]})
        
      • Response:
         HTTP/1.1 200 OK
         content-type: application/json
        [
        {'x_coord': '38.64261790634527', 'y_coord': '37.27661132812501'}
        ]
        
          
    • Any other significant work related to this release

    • Challenges

      • Frontend: Creating map is very crucial for my API. I chose leaflet for it, however it was very challenging. I had to close the server rendering in order to render it on the browser.

2.11. Ramazan Burak Sarıtaş

  • Links to important issues

  • The name, route, description of utilized third party URIs

  • The name, route, description of the created API functions

    • get_measurements
      • Route: /aqdata
      • Description: This function uses the OpenAQ API to fetch the latest air quality measurements. It also handles filtering and sorting of these measurements based on the parameters provided.
    • filter_and_sort_resources_mongo
      • Route: /resources
      • Description: This function retrieves resources from the MongoDB collection 'Resources'. It allows filtering and sorting of these resources based on the parameters provided.
    • create_resource
      • Route: /resources
      • Description: This function creates a new resource in the MongoDB 'Resources' collection.
    • delete_resource
      • Route: /resources/{resource_id}
      • Description: This function deletes a specific resource in the MongoDB 'Resources' collection.
    • clear_resources
      • Route: /resources/clear
      • Description: This function deletes all resources in the MongoDB 'Resources' collection.
  • Description of unit tests. You can include code fragments to illustrate.

    • Tests similar to the following were made during the implementation:
      •  def test_get_measurements_success():
          response = client.get("/aqdata")
          assert response.status_code == 200
        
         def test_get_measurements_fail():
          response = client.get("/aqdata", params={"country": "nonexistent"})
          assert response.status_code == 404
  • Sample calls

    • Request to /aqdata:
       curl -X GET "http://localhost:8000/aqdata?country=TR&city=Denizli&sort=desc"
    • Response:
      HTTP/1.1 200 OK
      content-type: application/json
      [
       {
         "country": "TR",
         "city": "Denizli",
         "measurement": "pm25",
         "lastUpdated": "2023-04-12T10:30:00Z"
       },
       ...
      ]
    • Filter & Sort request to /resources:
       curl -X GET "http://localhost:8000/resources?type=book&sort_by=upvotes"
    • Response:
       HTTP/1.1 200 OK
       content-type: application/json
        [
         {
          "id": "1",
          "type": "book", # filtered type
          "location": "Location1",
          "notes": "Notes1",
          "updated_at": "2023-04-12T10:30:00Z",
          "is_active": true,
          "upvotes": 10,  # sorted field
          "downvotes": 2,
          "creator_id": "Creator1",
          "creation_date": "2023-04-12T10:30:00Z",
          "condition": "Good",
          "quantity": 5
         }, {
          "id": "3",
          "type": "book", # filtered type
          "location": "Location1",
          "notes": "Notes1",
          "updated_at": "2023-04-12T10:30:00Z",
          "is_active": true,
          "upvotes": 16, # sorted field
          "downvotes": 2,
          "creator_id": "Creator7",
          "creation_date": "2023-04-10T10:30:00Z",
          "condition": "Good",
          "quantity": 4
         }
         ...
        ]
  • Any other significant work related to this release

  • Challenges

    • Connecting to Server: One of the initial challenges was to establish a connection to the server. This process involved understanding how to configure the server correctly and working with different settings and configurations. It was a new experience for me and took a considerable amount of time and effort.
    • API Implementation: The implementation of the API backend was another significant challenge. Understanding how to correctly use FastAPI, work with MongoDB, and manipulate data according to the requirements was not straightforward for me personally. Despite these challenges, with the collaborative effort and support from my team members, especially Egecan and Merve, I was able to overcome them. Their guidance was instrumental in understanding the concepts and executing the tasks effectively.
    • Frontend: Unfortunately, due to time constraints and the complexity of the tasks, I was unable to complete the frontend for this release. This is an area I aim to focus on and improve in future projects.
⚠️ **GitHub.com Fallback** ⚠️