Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
sd-20 committed Dec 6, 2022
2 parents 96cac57 + ccc0c41 commit cf9ac36
Show file tree
Hide file tree
Showing 15 changed files with 322 additions and 81 deletions.
32 changes: 20 additions & 12 deletions src/backend/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ def login():
if user_login.login(username, password):
# session object makes User accessible in the backend
session["username"] = username
print(session.get("username"))
return f"welcome {username}", 200
return "User not found, please try again", 401
return "", 400
Expand All @@ -57,7 +56,6 @@ def register():
@app.route("/user", methods=["GET", "POST"])
def userpage():
"""Handles userpage requests"""
print(session.get("username"))
if session.get("username") is None:
return "user does not exist", 403
name = session.get("username") or ""
Expand Down Expand Up @@ -97,7 +95,6 @@ def logout():
@app.route("/api/whoami")
def whoami():
"""Shows whether a user is logged in and returns session username"""
print(session.get("username"))
if session.get("username") is None:
return "user logged out", 403
username = session.get("username", "")
Expand All @@ -109,10 +106,10 @@ def whoami():
def mainpage():
"""Handle mainpage requests"""
mainpage_obj = MainPage()
args = request.args
if request.method == "POST":
return mainpage_post(mainpage_obj)
return mainpage_post(mainpage_obj, args)

args = request.args
return mainpage_get(mainpage_obj, args)


Expand All @@ -138,6 +135,7 @@ def mainpage_get(mainpage_obj: MainPage, args: MultiDict):
args.get("searchQuery", type=str),
args.get("priceSort", type=int),
args.get("ratingSort", type=int),
args.get("checkReview", type=bool),
)

return mainpage_process_get(mainpage_obj, action, param)
Expand All @@ -147,10 +145,15 @@ def mainpage_process_get(
mainpage_obj: MainPage, action: GetRequestType, param: GetRequestParams
):
"""Process the get requests"""
user = session.get("username", "")
query_result = ""
if action.is_search is True and param.search_query is not None:
apts = mainpage_obj.search_apartments(param.search_query)
query_result = dataclasses_into_json(apts)
if action.is_search is True:
if param.search_query is not None:
apts = mainpage_obj.search_apartments(param.search_query)
query_result = dataclasses_into_json(apts)
if param.apt_id is not None:
apt = mainpage_obj.get_single_apt(param.apt_id)
query_result = json.dumps(dataclasses.asdict(apt))

elif action.is_populate is True and param.num_apts is not None:
apts = []
Expand All @@ -171,8 +174,12 @@ def mainpage_process_get(
query_result = dataclasses_into_json(apts)

elif action.is_review is True and param.apt_id is not None:
reviews = mainpage_obj.get_apartments_reviews(param.apt_id)
query_result = dataclasses_into_json(reviews)
if param.check_review is None:
reviews = mainpage_obj.get_apartments_reviews(param.apt_id, user)
query_result = dataclasses_into_json(reviews)
else:
if mainpage_obj.check_user_reviewed(param.apt_id, user):
query_result = "True"

elif action.is_pictures is True and param.apt_id is not None:
query_result = json.dumps(mainpage_obj.get_apartments_pictures(param.apt_id))
Expand All @@ -182,15 +189,16 @@ def mainpage_process_get(
return "", 400


def mainpage_post(mainpage_obj: MainPage):
def mainpage_post(mainpage_obj: MainPage, args: MultiDict):
"""
Helper for mainpage post requests
Actions that use post request:
- Writing a review to an apartment
- Delete an existing review
"""
json_form = request.get_json(force=True)

is_delete = request.args.get("delete", default=False, type=bool)
is_delete = args.get("delete", default=False, type=bool)

if isinstance(json_form, dict):
if not is_delete:
Expand Down
Binary file modified src/backend/database/database_prod.db
Binary file not shown.
Binary file modified src/backend/database/database_test.db
Binary file not shown.
1 change: 1 addition & 0 deletions src/backend/dataholders/mainpage_get.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,4 @@ class GetRequestParams:
search_query: Union[str, None]
rating_sort: Union[int, None]
price_sort: Union[int, None]
check_review: Union[bool, None]
78 changes: 58 additions & 20 deletions src/backend/pages/mainpage.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ class MainPage:
FROM Apartments LEFT JOIN Reviews ON Apartments.apt_id = Reviews.apt_id \
GROUP BY Apartments.apt_id) "

search_apt_query = "SELECT Apartments.apt_id, Apartments.apt_name, Apartments.apt_address, \
COALESCE(SUM(Reviews.vote), 0) AS 'total_vote', \
Apartments.price_min, Apartments.price_max \
FROM Apartments LEFT JOIN Reviews ON Apartments.apt_id = Reviews.apt_id "

def __init__(self) -> None:
"""Constructor"""

Expand All @@ -35,11 +40,8 @@ def search_apartments(self, query: str) -> List[Apt]:
"""Returns a list of apartments with name matching query"""
query_sql = "%" + query.lower() + "%"
apt_query = self.search_apartments.cursor.execute(
"SELECT Apartments.apt_id, Apartments.apt_name, Apartments.apt_address, \
COALESCE(SUM(Reviews.vote), 0) AS 'total_vote', \
Apartments.price_min, Apartments.price_max \
FROM Apartments LEFT JOIN Reviews ON Apartments.apt_id = Reviews.apt_id \
WHERE LOWER(Apartments.apt_name) LIKE ? \
self.search_apt_query
+ "WHERE LOWER(Apartments.apt_name) LIKE ? \
GROUP BY Apartments.apt_id",
(query_sql,),
).fetchall()
Expand Down Expand Up @@ -216,13 +218,10 @@ def get_apartments_pictures(self, apt_id: int) -> List[str]:

@use_database
def write_apartment_review(
self, apt_id: int, username: str, comment: str, vote: int
self, apt_id: int, user: str, comment: str, vote: int
) -> List[Review]:
"""Write a new review for apartment"""
user_id = self.write_apartment_review.cursor.execute(
"SELECT user_id FROM Users WHERE username = ? OR email = ?",
(username, username),
).fetchone()[0]
user_id = self.get_user_id_from_user(user)
today = date.today().strftime("%Y-%m-%d")
self.write_apartment_review.cursor.execute(
"INSERT INTO Reviews (apt_id, user_id, date_of_rating, comment, vote) \
Expand All @@ -239,21 +238,23 @@ def write_apartment_review(
FROM Users INNER JOIN Reviews \
ON Users.user_id = Reviews.user_id \
WHERE Reviews.apt_id = ? \
ORDER BY Users.username = ? DESC, Reviews.date_of_rating DESC",
(apt_id, username),
ORDER BY Users.user_id = ? DESC, Reviews.date_of_rating DESC",
(apt_id, user_id),
).fetchall()
return self.create_reviews_helper(ratings_query)

@use_database
def get_apartments_reviews(self, apt_id: int) -> List[Review]:
def get_apartments_reviews(self, apt_id: int, user: str) -> List[Review]:
"""Returns a list of apartment reviews"""
user_id = self.get_user_id_from_user(user)
ratings_query = self.get_apartments_reviews.cursor.execute(
"SELECT Users.username, Reviews.date_of_rating, Reviews.comment, Reviews.vote \
FROM Users INNER JOIN Reviews \
ON Users.user_id = Reviews.user_id \
WHERE Reviews.apt_id = ? \
ORDER BY Reviews.date_of_rating DESC",
(apt_id,),
ORDER BY Users.user_id = ? DESC, \
Reviews.date_of_rating DESC",
(apt_id, user_id),
).fetchall()
return self.create_reviews_helper(ratings_query)

Expand All @@ -269,12 +270,49 @@ def create_reviews_helper(self, ratings_query: List[Tuple]) -> List[Review]:
return reviews

@use_database
def delete_apartment_review(self, apt_id: int, username: str) -> List[Review]:
def delete_apartment_review(self, apt_id: int, user: str) -> List[Review]:
"""Delete an apartment reviews"""
user_id = self.get_user_id_from_user(user)
self.delete_apartment_review.cursor.execute(
"DELETE FROM Reviews WHERE (apt_id = ? AND user_id = \
(SELECT user_id FROM Users WHERE username = ?))",
(apt_id, username),
"DELETE FROM Reviews WHERE (apt_id = ? AND user_id = ?)",
(apt_id, user_id),
)
self.delete_apartment_review.connection.commit()
return self.get_apartments_reviews(apt_id)
return self.get_apartments_reviews(apt_id, "")

@use_database
def check_user_reviewed(self, apt_id: int, user: str) -> bool:
"""Check if review exists for an user"""
user_id = self.get_user_id_from_user(user)
print(user_id)
review = self.check_user_reviewed.cursor.execute(
"SELECT * FROM Reviews WHERE (apt_id = ? AND user_id = ?)",
(
apt_id,
user_id,
),
).fetchone()
print(review)
return review is not None

@use_database
def get_user_id_from_user(self, user) -> int:
"""Gets user_id corresponding to certain username/email"""
user_id = self.get_user_id_from_user.cursor.execute(
"SELECT user_id FROM Users WHERE username = ? OR email = ?",
(user, user),
).fetchone()
if user_id is None:
return -1
return user_id[0]

@use_database
def get_single_apt(self, apt_id: int) -> Apt:
"""Get a single apt from apt id"""
apt = self.get_single_apt.cursor.execute(
self.search_apt_query
+ "WHERE Apartments.apt_id = ? \
GROUP BY Apartments.apt_id",
(apt_id,),
).fetchone()
return Apt(apt[0], apt[1], apt[2], apt[3], apt[4], apt[5])
38 changes: 38 additions & 0 deletions src/backend/tests/test_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,44 @@ def test_mainpage_get_valid_review(client):
assert res.text == sample_json


@use_test
def test_mainpage_get_user_reviewed(client):
"""Mainpage handles review checking request"""
mainpage = MainPageStaging()
mainpage.initialize_all()
connection = sqlite3.connect("database/database_test.db")
cursor = connection.cursor()
far_id = cursor.execute(
"SELECT apt_id FROM Apartments WHERE (apt_name = 'FAR')"
).fetchone()[0]
query = {"review": "True", "checkReview": True, "aptId": far_id}
log_info = {"user": "Big_finger", "password": "big_password1"}
res_1 = client.get("/login", json=log_info)
res = client.get("/main", query_string=query)
mainpage.clean_all()
connection.close()
assert res_1.status_code == 200
assert res.status_code == 200


@use_test
def test_mainpage_get_search_single_apt(client):
"""Mainpage handles getting single apt info"""
mainpage = MainPageStaging()
mainpage.initialize_all()
connection = sqlite3.connect("database/database_test.db")
cursor = connection.cursor()
far_id = cursor.execute(
"SELECT apt_id FROM Apartments WHERE (apt_name = 'FAR')"
).fetchone()[0]
query = {"search": "True", "aptId": far_id}
res = client.get("/main", query_string=query)
mainpage.clean_all()
connection.close()
print(res.text)
assert res.status_code == 200


@use_test
def test_mainpage_get_valid_search(client):
"""Test mainpage handles valid search request"""
Expand Down
33 changes: 31 additions & 2 deletions src/backend/tests/test_mainpage.py
Original file line number Diff line number Diff line change
Expand Up @@ -327,7 +327,7 @@ def test_get_apartments_reviews(self):
"SELECT apt_id FROM Apartments WHERE (apt_name = 'Sherman')"
).fetchone()[0]
connection.close()
res = self.main_page.get_apartments_reviews(sherman_id)
res = self.main_page.get_apartments_reviews(sherman_id, "Big_finger")

self.main_page_stage.clean_all()
assert sample_apts_review == res
Expand Down Expand Up @@ -375,7 +375,7 @@ def test_get_apartments_reviews_empty(self):
).fetchone()[0]
self.main_page_stage.clean_up_reviews(cursor, connection)
connection.close()
res = self.main_page.get_apartments_reviews(sherman_id)
res = self.main_page.get_apartments_reviews(sherman_id, "")
self.main_page_stage.clean_all()
assert sample_apts_review == res

Expand All @@ -402,3 +402,32 @@ def test_delete_apartment_review(self):
connection.close()
self.main_page_stage.clean_all()
assert modified_review == sample_apts_review

@use_test
def test_check_user_reviewed(self):
"""Test checking a review exists for a user"""
self.main_page_stage.initialize_all()
connection = sqlite3.connect("database/database_test.db")
cursor = connection.cursor()
par_id = cursor.execute(
"SELECT apt_id FROM Apartments WHERE (apt_name = 'PAR')"
).fetchone()[0]
check = self.main_page.check_user_reviewed(par_id, "Big_finger")
self.main_page_stage.clean_all()
connection.close()
assert check

@use_test
def test_get_single_apt(self):
"""Test gets a single apt"""
self.main_page_stage.initialize_all()
connection = sqlite3.connect("database/database_test.db")
cursor = connection.cursor()
par_id = cursor.execute(
"SELECT apt_id FROM Apartments WHERE (apt_name = 'PAR')"
).fetchone()[0]
par = Apt(par_id, "PAR", "901 W College Ct", -1, 5000, 6000)
check = self.main_page.get_single_apt(par_id)
self.main_page_stage.clean_all()
connection.close()
assert par == check
44 changes: 40 additions & 4 deletions src/frontend/src/components/SearchBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,42 @@ import React, { useState } from 'react';
import { useSearchParams } from 'react-router-dom';
import './SearchBarStyles.css';
import getSuggestions from './mainpageleft/getSearchBarSuggestions';
import { AptType } from './Types';
import axios from 'axios';
interface Props {
handleAptChange: (apt: AptType) => void;
}

export default function SearchBar() {
const baseURL = 'http://127.0.0.1:5000/main';
export default function SearchBar({ handleAptChange }: Props) {
const [query, setQuery] = useState('');
const [searchParams, setSearchParams] = useSearchParams();
const [search, setSearch] = useState(false);
const { names } = getSuggestions(query, search);

const { apts } = getSuggestions(query, search);
const queryApt = (id: number) => {
axios({
method: 'GET',
url: `${baseURL}?search=True&aptId=${id}`,
})
.then((response) => {
console.log(response);
handleAptChange({
id: response.data.apt_id,
name: response.data.name,
address: response.data.address,
price_min: response.data.price_min,
price_max: response.data.price_max,
rating: response.data.rating,
});
})
.catch((error) => {
if (error.response) {
console.log(error.response);
console.log(error.response.status);
console.log(error.response.headers);
}
});
};
const handleChange = (
event: React.SyntheticEvent<Element, Event>,
value: string
Expand All @@ -35,7 +64,14 @@ export default function SearchBar() {
id="free-solo-demo"
freeSolo
onInputChange={handleChange}
options={names.map((option) => option.name)}
onChange={(event, value) => {
console.log(event);
queryApt((value as AptType).id);
}}
options={apts}
getOptionLabel={(option) => (option as AptType).name}
//.map((option) => option.name)
filterOptions={(x) => x}
renderInput={(params) => (
<TextField {...params} label="Search apartments" />
)}
Expand Down
Loading

3 comments on commit cf9ac36

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Coverage

Coverage Report
FileStmtsMissCover
app.py1500100%
auth.py90100%
config.py10100%
decorators.py270100%
dataholders
   apt.py90100%
   mainpage_get.py160100%
   review.py70100%
   user.py80100%
pages
   login.py290100%
   mainpage.py1200100%
   userpage.py530100%
TOTAL4290100%

Tests Skipped Failures Errors Time
53 0 💤 0 ❌ 0 🔥 0.819s ⏱️

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Coverage

Coverage Report
FileStmtsMissCover
app.py1500100%
auth.py90100%
config.py10100%
decorators.py270100%
dataholders
   apt.py90100%
   mainpage_get.py160100%
   review.py70100%
   user.py80100%
pages
   login.py290100%
   mainpage.py1200100%
   userpage.py530100%
TOTAL4290100%

Tests Skipped Failures Errors Time
53 0 💤 0 ❌ 0 🔥 1.155s ⏱️

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Coverage

Coverage Report
FileStmtsMissCover
app.py1500100%
auth.py90100%
config.py10100%
decorators.py270100%
dataholders
   apt.py90100%
   mainpage_get.py160100%
   review.py70100%
   user.py80100%
pages
   login.py290100%
   mainpage.py1200100%
   userpage.py530100%
TOTAL4290100%

Tests Skipped Failures Errors Time
53 0 💤 0 ❌ 0 🔥 0.864s ⏱️

Please sign in to comment.