Skip to content

Commit a3b679c

Browse files
authored
Merge pull request #152 from Seluj78/like-profile
2 parents 7922a7b + c7dbd20 commit a3b679c

27 files changed

+966
-193
lines changed

PyMatcha.postman_collection.json

Lines changed: 674 additions & 18 deletions
Large diffs are not rendered by default.

backend/PyMatcha/__init__.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@
9898

9999
@jwt.expired_token_loader
100100
def expired_token_callback(expired_token):
101-
logging.error("Token {} expired".format(expired_token))
101+
logging.warning("Token {} expired".format(expired_token))
102102
resp = {
103103
"code": 401,
104104
"error": {
@@ -156,14 +156,13 @@ def expired_token_callback(expired_token):
156156
decode_responses=True,
157157
)
158158

159-
import PyMatcha.models.user as user_module
159+
from PyMatcha.models.user import get_user
160160

161-
get_user = user_module.get_user
162161

163162
logging.debug("Configuring JWT user callback loader")
164163

165164

166-
from PyMatcha.errors import NotFoundError
165+
from PyMatcha.utils.errors import NotFoundError
167166

168167

169168
@jwt.user_loader_callback_loader
@@ -190,12 +189,14 @@ def check_if_token_is_revoked(decrypted_token):
190189
from PyMatcha.routes.api.user import user_bp
191190
from PyMatcha.routes.api.auth import auth_bp
192191
from PyMatcha.routes.api.profile import profile_bp
192+
from PyMatcha.routes.api.like import like_bp
193193

194194
logging.debug("Registering Flask blueprints")
195195
application.register_blueprint(ping_pong_bp)
196196
application.register_blueprint(user_bp)
197197
application.register_blueprint(auth_bp)
198198
application.register_blueprint(profile_bp)
199+
application.register_blueprint(like_bp)
199200

200201
if application.debug:
201202
logging.debug("Registering debug route")
Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -18,35 +18,38 @@
1818
"""
1919
from __future__ import annotations
2020

21+
import datetime
2122
import logging
22-
from datetime import datetime
2323

24-
from PyMatcha.utils import create_user_images_table
24+
from PyMatcha.utils import create_likes_table
2525
from PyMatcha.utils.orm import Field
2626
from PyMatcha.utils.orm import Model
2727

2828

29-
class UserImage(Model):
30-
table_name = "user_images"
29+
class Like(Model):
30+
table_name = "likes"
3131

3232
id = Field(int, modifiable=False)
33-
user_id = Field(int)
34-
description = Field(str)
35-
timestamp = Field(str)
36-
is_primary = Field(bool)
33+
liker_id = Field(int)
34+
liked_id = Field(int)
35+
dt_liked = Field(datetime.datetime, fmt="%Y-%m-%d %H:%M:%S")
36+
is_superlike = Field(bool)
3737

3838
def before_init(self, data):
3939
pass
4040

4141
@staticmethod
4242
def create(
43-
user_id: int, description="", timestamp=datetime.timestamp(datetime.utcnow()), is_primary=False
44-
) -> UserImage:
45-
new_image = UserImage(user_id=user_id, description=description, timestamp=str(timestamp), is_primary=is_primary)
46-
new_image.save()
47-
logging.debug("Creating new user image")
48-
return new_image
43+
liker_id: int,
44+
liked_id: int,
45+
is_superlike: str = False,
46+
dt_liked: datetime.datetime = datetime.datetime.utcnow(),
47+
) -> Like:
48+
new_like = Like(liker_id=liker_id, liked_id=liked_id, is_superlike=is_superlike, dt_liked=dt_liked)
49+
new_like.save()
50+
logging.debug("Creating new like")
51+
return new_like
4952

5053
@classmethod
5154
def create_table(cls):
52-
create_user_images_table(cls.db)
55+
create_likes_table(cls.db)

backend/PyMatcha/models/report.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ class Report(Model):
3232
id = Field(int, modifiable=False)
3333
reporter_id = Field(int)
3434
reported_id = Field(int)
35-
dt_reported = Field(datetime.datetime)
35+
dt_reported = Field(datetime.datetime, fmt="%Y-%m-%d %H:%M:%S")
3636
details = Field(str)
3737
reason = Field(str)
3838
status = Field(str)

backend/PyMatcha/models/user.py

Lines changed: 68 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -23,23 +23,19 @@
2323
import logging
2424
from typing import Any
2525
from typing import Dict
26-
from typing import List
2726
from typing import Optional
2827

2928
import Geohash
30-
import PyMatcha.models.user_image as user_image
31-
from PyMatcha.errors import ConflictError
32-
from PyMatcha.errors import NotFoundError
3329
from PyMatcha.models.report import Report
3430
from PyMatcha.models.tag import Tag
3531
from PyMatcha.models.view import View
3632
from PyMatcha.utils import create_user_table
3733
from PyMatcha.utils import hash_password
34+
from PyMatcha.utils.errors import ConflictError
35+
from PyMatcha.utils.errors import NotFoundError
3836
from PyMatcha.utils.orm import Field
3937
from PyMatcha.utils.orm import Model
4038

41-
UserImage = user_image.UserImage
42-
4339

4440
class User(Model):
4541
table_name = "users"
@@ -170,7 +166,7 @@ def register(email: str, username: str, password: str, first_name: str, last_nam
170166
except ValueError:
171167
pass
172168
else:
173-
logging.error("Email {} taken".format(email))
169+
logging.warning("Email {} taken".format(email))
174170
raise ConflictError("Email {} taken".format(email), "Use another email")
175171

176172
# Check username availability
@@ -215,32 +211,17 @@ def to_dict(self) -> Dict:
215211
returned_dict["reports"] = {"sent": [], "received": []}
216212
returned_dict["reports"]["sent"] = [r.to_dict() for r in self.get_reports_sent()]
217213
returned_dict["reports"]["received"] = [r.to_dict() for r in self.get_reports_received()]
214+
returned_dict["likes"] = {"sent": [], "received": []}
215+
returned_dict["likes"]["sent"] = [l.to_dict() for l in self.get_likes_sent()]
216+
returned_dict["likes"]["received"] = [l.to_dict() for l in self.get_likes_received()]
217+
returned_dict.pop("password")
218+
returned_dict.pop("previous_reset_token")
218219
return returned_dict
219220

220221
@classmethod
221222
def create_table(cls):
222223
create_user_table(cls.db)
223224

224-
def get_images(self) -> List[UserImage]:
225-
logging.debug("Getting all images for user {}".format(self.id))
226-
with self.db.cursor() as c:
227-
c.execute(
228-
"""
229-
SELECT user_images.id as id, user_images.user_id as user_id, user_images.description as description,
230-
user_images.timestamp as timestamp, user_images.is_primary as is_primary
231-
FROM users
232-
INNER JOIN user_images on users.id = user_images.user_id
233-
WHERE users.id = CAST({} AS UNSIGNED)
234-
""".format(
235-
self.id
236-
)
237-
)
238-
images = c.fetchall()
239-
image_list = []
240-
for image in images:
241-
image_list.append(UserImage(image))
242-
return image_list
243-
244225
def get_jwt_info(self):
245226
return {
246227
"id": self.id,
@@ -331,6 +312,65 @@ def get_reports_sent(self):
331312
reports_list.append(Report(r))
332313
return reports_list
333314

315+
def get_likes_received(self):
316+
logging.debug("Getting all likes received for user {}".format(self.id))
317+
with self.db.cursor() as c:
318+
c.execute(
319+
"""
320+
SELECT likes.id as id, likes.liked_id as liked_id,
321+
likes.liker_id as liker_id, likes.dt_liked as dt_liked,
322+
likes.is_superlike as is_superlike
323+
FROM users
324+
INNER JOIN likes on users.id = likes.liked_id
325+
WHERE users.id = CAST({} AS UNSIGNED)
326+
""".format(
327+
self.id
328+
)
329+
)
330+
likes = c.fetchall()
331+
like_list = []
332+
for l in likes:
333+
like_list.append(Report(l))
334+
return like_list
335+
336+
def get_likes_sent(self):
337+
logging.debug("Getting all likes sent for user {}".format(self.id))
338+
with self.db.cursor() as c:
339+
c.execute(
340+
"""
341+
SELECT likes.id as id, likes.liked_id as liked_id,
342+
likes.liker_id as liker_id, likes.dt_liked as dt_liked,
343+
likes.is_superlike as is_superlike
344+
FROM users
345+
INNER JOIN likes on users.id = likes.liker_id
346+
WHERE users.id = CAST({} AS UNSIGNED)
347+
""".format(
348+
self.id
349+
)
350+
)
351+
likes = c.fetchall()
352+
like_list = []
353+
for l in likes:
354+
like_list.append(Report(l))
355+
return like_list
356+
357+
def already_likes(self, liked_id: int) -> bool:
358+
with self.db.cursor() as c:
359+
c.execute(
360+
"""
361+
SELECT EXISTS(
362+
SELECT * FROM likes WHERE
363+
liker_id = CAST({} AS UNSIGNED) and
364+
liked_id = CAST({} AS UNSIGNED)
365+
)
366+
""".format(
367+
self.id, liked_id
368+
)
369+
)
370+
result = c.fetchone()
371+
value = next(iter(result.values()))
372+
return bool(value)
373+
334374

335375
def get_user(uid: Any[int, str]) -> Optional[User]:
336376
not_found = 0
@@ -360,7 +400,7 @@ def get_user(uid: Any[int, str]) -> Optional[User]:
360400
f_user = user
361401
# If none of those worked, throw an error
362402
if not_found == 3:
363-
logging.error("User {} not found.".format(uid))
403+
logging.warning("User {} not found.".format(uid))
364404
raise NotFoundError("User {} not found.".format(uid), "Try again with another uid")
365405
logging.debug("Found user {} from {}".format(f_user.id, uid))
366406
return f_user

backend/PyMatcha/routes/api/auth.py

Lines changed: 29 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -19,32 +19,37 @@
1919
import datetime
2020
import os
2121

22-
import flask_jwt_extended as fjwt
23-
import PyMatcha.models.user as user
2422
from flask import Blueprint
2523
from flask import current_app
2624
from flask import redirect
2725
from flask import request
26+
from flask_jwt_extended import create_access_token
27+
from flask_jwt_extended import create_refresh_token
28+
from flask_jwt_extended import get_jti
29+
from flask_jwt_extended import get_jwt_identity
30+
from flask_jwt_extended import get_raw_jwt
31+
from flask_jwt_extended import jwt_refresh_token_required
32+
from flask_jwt_extended import jwt_required
2833
from itsdangerous import BadSignature
2934
from itsdangerous import SignatureExpired
3035
from PyMatcha import ACCESS_TOKEN_EXPIRES
3136
from PyMatcha import redis
3237
from PyMatcha import REFRESH_TOKEN_EXPIRES
33-
from PyMatcha.errors import BadRequestError
34-
from PyMatcha.errors import ConflictError
35-
from PyMatcha.errors import NotFoundError
36-
from PyMatcha.errors import UnauthorizedError
37-
from PyMatcha.success import Success
38-
from PyMatcha.success import SuccessOutput
39-
from PyMatcha.success import SuccessOutputMessage
38+
from PyMatcha.models.user import get_user
39+
from PyMatcha.models.user import User
4040
from PyMatcha.utils import hash_password
4141
from PyMatcha.utils.confirm_token import confirm_token
4242
from PyMatcha.utils.confirm_token import generate_confirmation_token
4343
from PyMatcha.utils.decorators import validate_params
44+
from PyMatcha.utils.errors import BadRequestError
45+
from PyMatcha.utils.errors import ConflictError
46+
from PyMatcha.utils.errors import NotFoundError
47+
from PyMatcha.utils.errors import UnauthorizedError
4448
from PyMatcha.utils.mail import send_mail_text
49+
from PyMatcha.utils.success import Success
50+
from PyMatcha.utils.success import SuccessOutput
51+
from PyMatcha.utils.success import SuccessOutputMessage
4552

46-
User = user.User
47-
get_user = user.get_user
4853

4954
REQUIRED_KEYS_USER_CREATION = {"username": str, "email": str, "password": str, "first_name": str, "last_name": str}
5055
REQUIRED_KEYS_PASSWORD_FORGOT = {"email": str}
@@ -71,7 +76,7 @@ def api_create_user():
7176
last_name=data["last_name"],
7277
)
7378
except ConflictError as e:
74-
current_app.logger.error("Conflict error on user register: {}".format(e))
79+
current_app.logger.warning("Conflict error on user register: {}".format(e))
7580
raise e
7681
else:
7782
token = generate_confirmation_token(email=data["email"], token_type="confirm")
@@ -192,10 +197,10 @@ def auth_login():
192197
if not u.is_confirmed:
193198
current_app.logger.debug("/auth/login -> User is trying to login unconfirmed")
194199
raise UnauthorizedError("User needs to be confirmed first.", "Try again when you have confirmed your email")
195-
access_token = fjwt.create_access_token(identity=u.get_jwt_info(), fresh=True)
196-
refresh_token = fjwt.create_refresh_token(identity=u.get_jwt_info())
197-
access_jti = fjwt.get_jti(access_token)
198-
refresh_jti = fjwt.get_jti(refresh_token)
200+
access_token = create_access_token(identity=u.get_jwt_info(), fresh=True)
201+
refresh_token = create_refresh_token(identity=u.get_jwt_info())
202+
access_jti = get_jti(access_token)
203+
refresh_jti = get_jti(refresh_token)
199204

200205
redis.set("jti:" + access_jti, "false", ACCESS_TOKEN_EXPIRES * 1.2)
201206
redis.set("jti:" + refresh_jti, "false", REFRESH_TOKEN_EXPIRES * 1.2)
@@ -207,27 +212,27 @@ def auth_login():
207212

208213

209214
@auth_bp.route("/auth/refresh", methods=["POST"])
210-
@fjwt.jwt_refresh_token_required
215+
@jwt_refresh_token_required
211216
def refresh():
212-
current_user = fjwt.get_jwt_identity()
213-
access_token = fjwt.create_access_token(identity=current_user)
214-
access_jti = fjwt.get_jti(encoded_token=access_token)
217+
current_user = get_jwt_identity()
218+
access_token = create_access_token(identity=current_user)
219+
access_jti = get_jti(encoded_token=access_token)
215220
redis.set("jti:" + access_jti, "false", ACCESS_TOKEN_EXPIRES * 1.2)
216221
return SuccessOutput("access_token", access_token)
217222

218223

219224
@auth_bp.route("/auth/access_revoke", methods=["DELETE"])
220-
@fjwt.jwt_required
225+
@jwt_required
221226
def logout():
222-
jti = fjwt.get_raw_jwt()["jti"]
227+
jti = get_raw_jwt()["jti"]
223228
redis.set("jti:" + jti, "true", ACCESS_TOKEN_EXPIRES * 1.2)
224229
return Success("Access token revoked")
225230

226231

227232
@auth_bp.route("/auth/refresh_revoke", methods=["DELETE"])
228-
@fjwt.jwt_refresh_token_required
233+
@jwt_refresh_token_required
229234
def logout2():
230-
jti = fjwt.get_raw_jwt()["jti"]
235+
jti = get_raw_jwt()["jti"]
231236
redis.set("jti:" + jti, "true", REFRESH_TOKEN_EXPIRES * 1.2)
232237
return Success("Refresh token revoked")
233238

0 commit comments

Comments
 (0)