Skip to content

Commit 3c4837b

Browse files
authored
Merge pull request #295 from Seluj78/feature/138-search
2 parents 0c66e20 + 1e3fa78 commit 3c4837b

File tree

8 files changed

+930
-33
lines changed

8 files changed

+930
-33
lines changed

PyMatcha.postman_collection.json

Lines changed: 703 additions & 23 deletions
Large diffs are not rendered by default.

backend/PyMatcha/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,7 @@ def check_if_token_is_revoked(decrypted_token):
227227
from PyMatcha.routes.api.messages import messages_bp
228228
from PyMatcha.routes.api.recommendations import recommendations_bp
229229
from PyMatcha.routes.api.profile.images import images_bp
230+
from PyMatcha.routes.api.search import search_bp
230231

231232
logging.debug("Registering Flask blueprints")
232233
application.register_blueprint(user_bp)
@@ -243,6 +244,7 @@ def check_if_token_is_revoked(decrypted_token):
243244
application.register_blueprint(messages_bp)
244245
application.register_blueprint(recommendations_bp)
245246
application.register_blueprint(images_bp)
247+
application.register_blueprint(search_bp)
246248

247249
if application.debug:
248250
logging.debug("Registering debug route")
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
import datetime
2+
3+
from flask import Blueprint
4+
from flask import request
5+
from flask_jwt_extended import current_user
6+
from flask_jwt_extended import jwt_required
7+
from PyMatcha.utils.decorators import validate_params
8+
from PyMatcha.utils.errors import BadRequestError
9+
from PyMatcha.utils.match_score import _get_common_tags
10+
from PyMatcha.utils.match_score import _get_distance
11+
from PyMatcha.utils.match_score import _get_gender_query
12+
from PyMatcha.utils.success import SuccessOutput
13+
14+
search_bp = Blueprint("search", __name__)
15+
16+
REQUIRED_PARAMS_SEARCH = {
17+
"min_age": int,
18+
"max_age": int,
19+
"min_score": int,
20+
"max_score": int,
21+
"tags": list,
22+
"max_distance": int,
23+
}
24+
25+
26+
@search_bp.route("/search", methods=["POST"])
27+
@validate_params(REQUIRED_PARAMS_SEARCH, allow_empty=True)
28+
@jwt_required
29+
def search():
30+
data = request.get_json()
31+
min_age = int(data["min_age"])
32+
max_age = int(data["max_age"])
33+
min_score = int(data["min_score"])
34+
max_score = int(data["max_score"])
35+
max_distance = int(data["max_distance"])
36+
tags = data["tags"]
37+
38+
today = datetime.datetime.utcnow()
39+
40+
query = _get_gender_query(current_user.orientation, current_user.gender)
41+
returned_list = []
42+
for user in query:
43+
user_age = (
44+
today.year - user.birthdate.year - ((today.month, today.day) < (user.birthdate.month, user.birthdate.day))
45+
)
46+
47+
if max_age != -1:
48+
if user_age >= max_age:
49+
continue
50+
if min_age != -1:
51+
if user_age <= min_age:
52+
continue
53+
54+
if max_score != -1:
55+
if user.heat_score >= max_score:
56+
continue
57+
if min_score != -1:
58+
if user.heat_score <= min_score:
59+
continue
60+
61+
distance = _get_distance(current_user.geohash, user.geohash)
62+
if distance:
63+
if distance >= max_distance:
64+
if max_distance != -1:
65+
continue
66+
else:
67+
if max_distance != -1:
68+
raise BadRequestError("user needs to sets his location first")
69+
70+
user_tags = [t.name for t in user.get_tags()]
71+
common_tags = _get_common_tags(tags, user_tags)
72+
if not common_tags:
73+
if tags:
74+
continue
75+
76+
user_dict = user.to_dict()
77+
user_dict.update({"distance": distance, "common_tags": common_tags})
78+
returned_list.append(user_dict)
79+
return SuccessOutput("search_results", returned_list)

backend/PyMatcha/utils/decorators.py

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
from werkzeug.exceptions import BadRequest
99

1010

11-
def validate_params(required: dict, optional: Optional[dict] = None):
11+
def validate_params(required: dict, optional: Optional[dict] = None, allow_empty=False):
1212
if optional is None:
1313
optional = {}
1414

@@ -43,12 +43,13 @@ def wrapper(*args, **kwargs):
4343
"You are only allowed to specify the fields {}" ".".format(required.keys()),
4444
)
4545

46-
for key, value in data.items():
47-
if not value:
48-
if required[key] == int or required[key] == bool:
49-
pass
50-
else:
51-
raise BadRequestError(f"The item {key} cannot be None or empty.")
46+
if not allow_empty:
47+
for key, value in data.items():
48+
if not value:
49+
if required[key] == int or required[key] == bool:
50+
pass
51+
else:
52+
raise BadRequestError(f"The item {key} cannot be None or empty.")
5253

5354
wrong_types = [r for r in required.keys() if not isinstance(data[r], required[r])]
5455
wrong_types += [r for r in optional.keys() if r in data and not isinstance(data[r], optional[r])]

backend/PyMatcha/utils/populate_database.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ def populate_users(amount=150, drop_user_table=False):
4545
date_lastseen = gen_datetime(min_year=2017, max_year=datetime.datetime.now().year)
4646
username = user.get_username()
4747

48-
birthdate = datetime.datetime.strptime(user.get_dob(), "%Y-%m-%dT%H:%M:%S.%fZ").date()
48+
birthdate = gen_datetime(min_year=1985, max_year=datetime.datetime.now().year - 18).date()
4949
try:
5050
User.create(
5151
first_name=user.get_first_name(),
@@ -58,7 +58,7 @@ def populate_users(amount=150, drop_user_table=False):
5858
orientation=orientation,
5959
birthdate=birthdate,
6060
geohash=geohash,
61-
heat_score=0,
61+
heat_score=random.randint(0, 150),
6262
is_online=random.choice([True, False]),
6363
date_joined=date_joined,
6464
date_lastseen=date_lastseen,

backend/PyMatcha/utils/tasks.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ def update_heat_scores():
4848
score -= reports_received * REPORTS_MULTIPLIER
4949
score += views * VIEW_MULTIPLIER
5050
score += ceil(messages / MESSAGES_DIVIDER)
51+
if score < 0:
52+
score = 0
5153
user.heat_score = score
5254
user.save()
5355
return f"Updated heat score for user {user.id}: {user.heat_score}."

backend/schemas/swagger.yaml

Lines changed: 134 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ tags:
2020
- name: Messages
2121
description: Send and receive messages
2222
- name: Recommendations
23+
- name: Search
2324
- name: User
2425
description: Get user info
2526
- name: Debug
@@ -1204,6 +1205,36 @@ paths:
12041205
message:
12051206
type: string
12061207
example: Image successfully deleted.
1208+
/search:
1209+
post:
1210+
summary: Search users
1211+
operationId: searchUsers
1212+
security:
1213+
- bearerAuth: [ ]
1214+
tags:
1215+
- Search
1216+
responses:
1217+
"400":
1218+
$ref: '#/components/responses/BadRequest'
1219+
"200":
1220+
description: Returns a list of users based on the search
1221+
content:
1222+
application/json:
1223+
schema:
1224+
type: object
1225+
properties:
1226+
success:
1227+
type: boolean
1228+
description: If the request is a success
1229+
example: true
1230+
code:
1231+
type: integer
1232+
description: The status code
1233+
example: 200
1234+
search_results:
1235+
type: array
1236+
items:
1237+
$ref: '#/components/schemas/SearchResult'
12071238

12081239

12091240
servers:
@@ -1894,4 +1925,106 @@ components:
18941925
is_primary:
18951926
type: boolean
18961927
description: Is this image the profile picture of the user
1897-
example: true
1928+
example: true
1929+
SearchResult:
1930+
type: object
1931+
properties:
1932+
common_tags:
1933+
type: array
1934+
example: [ "friends", "drinks", "sun" ]
1935+
distance:
1936+
type: integer
1937+
example: 50
1938+
description: The distance between two users
1939+
id:
1940+
type: integer
1941+
example: 1
1942+
first_name:
1943+
type: string
1944+
example: Foo
1945+
last_name:
1946+
type: string
1947+
example: Bar
1948+
username:
1949+
type: string
1950+
example: FooBar
1951+
email:
1952+
type: string
1953+
example: foo@example.org
1954+
bio:
1955+
type: string
1956+
example: Lorem Ipsum is the single greatest threat. We are not - we are not keeping up with other websites. Lorem Ipsum best not make any more threats to your website. It will be met with fire and fury like the world has never seen. Does everybody know that pig named Lorem Ipsum? An ‘extremely credible source’ has called my office and told me that Barack Obama’s placeholder text is a fraud.
1957+
birthdate:
1958+
type: date
1959+
example: Mon, 06 Apr 1998 00:00:00 GMT
1960+
gender:
1961+
type: string
1962+
enum: [ "male", "female", "other" ]
1963+
example: male
1964+
orientation:
1965+
type: string
1966+
enum: [ "heterosexual", "homosexual", "bisexual", "other" ]
1967+
example: heterosexual
1968+
geohash:
1969+
type: string
1970+
example: u09whv25xgzn
1971+
heat_score:
1972+
type: integer
1973+
example: 123
1974+
confirmed_on:
1975+
type: date
1976+
example: Wed, 16 Sep 2020 15:20:02 GMT
1977+
date_joined:
1978+
type: date
1979+
example: Wed, 16 Sep 2020 15:18:02 GMT
1980+
date_lastseen:
1981+
type: date
1982+
example: Wed, 19 Sep 2020 19:24:02 GMT
1983+
is_confirmed:
1984+
type: boolean
1985+
example: true
1986+
is_online:
1987+
type: boolean
1988+
example: true
1989+
is_profile_completed:
1990+
type: boolean
1991+
example: true
1992+
age:
1993+
type: int
1994+
example: 22
1995+
likes:
1996+
type: object
1997+
properties:
1998+
sent:
1999+
type: array
2000+
items:
2001+
$ref: '#/components/schemas/Like'
2002+
uniqueItems: true
2003+
received:
2004+
type: array
2005+
items:
2006+
$ref: '#/components/schemas/Like'
2007+
uniqueItems: true
2008+
reports:
2009+
type: object
2010+
properties:
2011+
sent:
2012+
type: array
2013+
items:
2014+
$ref: '#/components/schemas/Report'
2015+
uniqueItems: true
2016+
received:
2017+
type: array
2018+
items:
2019+
$ref: '#/components/schemas/Report'
2020+
uniqueItems: true
2021+
tags:
2022+
type: array
2023+
items:
2024+
$ref: '#/components/schemas/Tag'
2025+
uniqueItems: true
2026+
images:
2027+
type: array
2028+
items:
2029+
$ref: '#/components/schemas/Image'
2030+
uniqueItems: true

subject.pdf

42.1 KB
Binary file not shown.

0 commit comments

Comments
 (0)