Individual Contribution Report 2 | Süleyman Melih Portakal - bounswe/bounswe2023group6 GitHub Wiki

Member

Name: Süleyman Melih Portakal

Main git issues (significant ones only)

  • Learning Flask and Git #150
  • Researching and implementing Dungeons and Dragons API #177
  • Implement the GET-POST Methods using the Dnd5 API #187
  • Revert "Implement the GET-POST Methods using the Dnd5 API" #190
  • implementing Melih/dnd-api-v2 #199
  • Adding tests for dndapi #203
  • added test_dnd_api #211
  • Form an individual contiribution report #214

The name, route, description of utilized third party URIs

The Dungeons and Dragons 5 API is a valuable resource for programmers and gaming enthusiasts looking to gather information on the various spells, monsters, rules, classes and races within the game. Link is here.

One of the routes provided by the Dungeons and Dragons 5 API is the https://www.dnd5eapi.co/api/classes/{class} route, which accepts a parameter for the desired class (Example: Ranger) and returns JSON data including information such as hit dice, proficiencies, and class features. In my API, I use name and hit dice fields.

Another route available is the https://www.dnd5eapi.co/api/races/{race} route, which provides information on various races within the game (Example: Elf) and returns JSON data including size information, traits, abilities, and subraces. In my API, I use name , speed, bonusses, age, alignment, language description and size description fields.

The name, route, description of the created API functions

  • Name: dnd
  • Route: /dnd
  • Description: this function render dnd.html using dnd_information. Dnd information include some race and class information
  • Code:
@app.route("/dnd", methods=["GET", "POST"])
def dnd(log=None):
    if request.method == "POST":
        race_name = request.form.get("race")
        if not race_name:
            log = "Race name is required!"
        class_name = request.form.get("class")
        if not class_name:
            log = "Class name is required!"
        else:
            dnd_information = get_dnd_information(race_name, class_name)
            if dnd_information == None:
                log = "Information can not be found! Try with a different race or class"
            else:
                return render_template("dnd.html", response=dnd_information)

    return render_template("dnd.html", log=log) 
  • Name: like_combination
  • Route: /like_combination
  • Description: This function post race-class combination to database and return a log about what happened.
  • Code:
@app.route("/like_combination", methods=["POST"])
def like_combination():
    race_name = request.form.get("race_name")
    class_name = request.form.get("class_name")
    log = None
    if not race_name or not class_name:
        log = "Problems occured!"
    else:
        previously_added = FavoriteRCCombination.query.filter_by(
            user_id=current_user.id, race_name=race_name, class_name=class_name
        ).all()
        if len(previously_added) != 0:
            log = "Combination already liked!"
        else:
            combination = FavoriteRCCombination(
                user_id=current_user.id, race_name=race_name, class_name=class_name
            )
            session.add(combination)
            session.commit()
            log = "Combination added!"
    return {"log": log}
  • Name: show_most_liked_combinations
  • Route: /show_most_liked_combinations
  • Description: this function render dnd.html using combinations. I get combinations and their like count from database.
  • Code:
@app.route("/show_most_liked_combinations", methods=["POST"])
def show_most_liked_combinations():
    counts = (
        FavoriteRCCombination.query.with_entities(
            FavoriteRCCombination.race_name,
            FavoriteRCCombination.class_name,
            func.count(FavoriteRCCombination.id).label("count"),
        )
        .group_by(FavoriteRCCombination.race_name, FavoriteRCCombination.class_name)
        .all()
    )

    counts.sort(key=lambda a: a.count, reverse = True)
    return render_template("dnd.html", combinations=counts)

Description of Unit Tests

  • Description: testing external api return a true response
  • Code:
def test_get_dnd_information(client):
    response = get_dnd_information("elf","barbarian")

    assert response["name"] == "Elf" and response["class_name"] == "Barbarian"
  • Description: testing whether /dnd route is working in "GET".
  • Code:
def test_get_dnd(client):
    response = client.get("/dnd")
    assert b"<h1>Dungeons and Dragons Race-Class Description</h1>" in response.data
  • Description: testing whether /dnd route is working in "POST". it controls status_code and it looks for a line in rendered dnd.html.
  • Code:
def test_post_dnd_info(client):
    response = client.post("/dnd", data={
    "race" : "elf",
    "class" : "barbarian"
    }, follow_redirects=True)

    assert response.status_code == 200

    assert b"Race: Elf" in response.data
    assert b"Class: Barbarian" in response.data
  • Description: testing whether /show_most_liked_combinationsr oute is working. it controls status_code and it looks for a line in dnd.html.
  • Code:
def test_show_most_liked(client):
    response = client.post("/show_most_liked_combinations", follow_redirects=True)

    assert response.status_code == 200
    assert b"<h1>Dungeons and Dragons Race-Class Description</h1>" in response.data
  • Description: testing whether /like_combination route is working. First it login to system. Then, it controls status_code and it looks for a message in returned log message.
  • Code:
def test_like_combination(client):
    client.post("/login", data=dict(
    username=TEST_USER, password=TEST_USERPASS
    ), follow_redirects=True)
    response = client.post("/like_combination", data={
    "race_name" : "elf",
    "class_name" : "barbarian"
    }, follow_redirects=True)

    assert response.status_code == 200
    assert  b"\"log\":\"Combination added!\"" in response.data
  • Description: it cleans databes
  • Code:
def test_clean(session):

    user = User.query.filter_by(username=TEST_USER).first()
    combin = FavoriteRCCombination.query.filter_by(user_id= user.id).all()
    if combin:
        for i in combin:
            session.delete(i)
            session.commit()

    if user:
        session.delete(user)
        session.commit()

Sample Calls

this link show sample calls for dnd api get class: here this link show sample calls for dnd api get races: here

Challenges that I encountered

The first challenge I encountered was learning Flask and Git because I had never written backend or frontend code before and had not used Git before. Therefore, I practiced and learned them through practical experience.

The biggest challenge I faced was when my friend Umut merged my pull request into the main branch, but the previous commits were deleted, so we had to revert the merge. Afterwards, I couldn't open a pull request for the same branch, so I created a new branch and opened a new pull request.

⚠️ **GitHub.com Fallback** ⚠️