Skip to content

Commit 13ecde7

Browse files
committed
env...
2 parents c61725e + 3a9b9f5 commit 13ecde7

File tree

14 files changed

+699
-254
lines changed

14 files changed

+699
-254
lines changed

.env.enc

0 Bytes
Binary file not shown.

PyMatcha.postman_collection.json

Lines changed: 279 additions & 147 deletions
Large diffs are not rendered by default.

backend/PyMatcha/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@
5959
"REDIS_PORT",
6060
"DEBUG_AUTH_TOKEN",
6161
"FRONTEND_BASE_URL",
62+
"IMGUR_CLIENT_ID",
63+
"IMGUR_CLIENT_SECRET",
6264
]
6365

6466
for item in REQUIRED_ENV_VARS:
@@ -224,6 +226,7 @@ def check_if_token_is_revoked(decrypted_token):
224226
from PyMatcha.routes.api.match import match_bp
225227
from PyMatcha.routes.api.messages import messages_bp
226228
from PyMatcha.routes.api.recommendations import recommendations_bp
229+
from PyMatcha.routes.api.profile.images import images_bp
227230

228231
logging.debug("Registering Flask blueprints")
229232
application.register_blueprint(user_bp)
@@ -239,6 +242,7 @@ def check_if_token_is_revoked(decrypted_token):
239242
application.register_blueprint(match_bp)
240243
application.register_blueprint(messages_bp)
241244
application.register_blueprint(recommendations_bp)
245+
application.register_blueprint(images_bp)
242246

243247
if application.debug:
244248
logging.debug("Registering debug route")

backend/PyMatcha/models/image.py

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
"""
2+
PyMatcha - A Python Dating Website
3+
Copyright (C) 2018-2019 jlasne/gmorer
4+
<jlasne@student.42.fr> - <lauris.skraucis@gmail.com>
5+
6+
This program is free software: you can redistribute it and/or modify
7+
it under the terms of the GNU General Public License as published by
8+
the Free Software Foundation, either version 3 of the License, or
9+
(at your option) any later version.
10+
11+
This program is distributed in the hope that it will be useful,
12+
but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
GNU General Public License for more details.
15+
16+
You should have received a copy of the GNU General Public License
17+
along with this program. If not, see <https://www.gnu.org/licenses/>.
18+
"""
19+
from __future__ import annotations
20+
21+
import logging
22+
from datetime import datetime
23+
24+
from PyMatcha.utils import create_images_table
25+
from PyMatcha.utils.orm import Field
26+
from PyMatcha.utils.orm import Model
27+
28+
29+
class Image(Model):
30+
table_name = "images"
31+
32+
id = Field(int, modifiable=False)
33+
user_id = Field(int)
34+
link = Field(str)
35+
timestamp = Field(datetime, fmt="%Y-%m-%d %H:%M:%S")
36+
is_primary = Field(bool)
37+
38+
@staticmethod
39+
def create(user_id: int, link: str, is_primary: bool = False, timestamp: datetime = datetime.utcnow()) -> Image:
40+
new_image = Image(user_id=user_id, link=link, is_primary=is_primary, timestamp=timestamp)
41+
new_image.save()
42+
logging.debug("Creating new image")
43+
return new_image
44+
45+
@classmethod
46+
def create_table(cls):
47+
create_images_table(cls.db)

backend/PyMatcha/models/user.py

Lines changed: 40 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
from typing import Optional
2727

2828
import Geohash
29+
from PyMatcha.models.image import Image
2930
from PyMatcha.models.like import Like
3031
from PyMatcha.models.match import Match
3132
from PyMatcha.models.message import Message
@@ -182,6 +183,7 @@ def register(email: str, username: str, password: str, first_name: str, last_nam
182183
def to_dict(self) -> Dict:
183184
returned_dict = super().to_dict()
184185
returned_dict["tags"] = [t.to_dict() for t in self.get_tags()]
186+
returned_dict["images"] = [image.to_dict() for image in self.get_images()]
185187
returned_dict["reports"] = {"sent": [], "received": []}
186188
returned_dict["reports"]["sent"] = [r.to_dict() for r in self.get_reports_sent()]
187189
returned_dict["reports"]["received"] = [r.to_dict() for r in self.get_reports_received()]
@@ -205,128 +207,61 @@ def get_jwt_info(self):
205207
"date_lastseen": self.date_lastseen,
206208
}
207209

210+
def get_images(self):
211+
logging.debug("Getting all images for user {}".format(self.id))
212+
image_list = Image.get_multis(user_id=self.id)
213+
if not image_list:
214+
return []
215+
else:
216+
return image_list
217+
208218
def get_tags(self):
209219
logging.debug("Getting all tags for user {}".format(self.id))
210-
with self.db.cursor() as c:
211-
c.execute(
212-
"""
213-
SELECT tags.id as id, tags.user_id as user_id, tags.name as name
214-
FROM users
215-
INNER JOIN tags on users.id = tags.user_id
216-
WHERE users.id = CAST({} AS UNSIGNED)
217-
""".format(
218-
self.id
219-
)
220-
)
221-
tags = c.fetchall()
222-
tags_list = []
223-
for t in tags:
224-
tags_list.append(Tag(t))
225-
return tags_list
220+
tag_list = Tag.get_multis(user_id=self.id)
221+
if not tag_list:
222+
return []
223+
else:
224+
return tag_list
226225

227226
def get_views(self):
228227
logging.debug("Getting all views for user profile {}".format(self.id))
229-
with self.db.cursor() as c:
230-
c.execute(
231-
"""
232-
SELECT views.id as id, views.profile_id as profile_id,
233-
views.viewer_id as viewer_id, views.dt_seen as dt_seen
234-
FROM users
235-
INNER JOIN views on users.id = views.profile_id
236-
WHERE users.id = CAST({} AS UNSIGNED)
237-
""".format(
238-
self.id
239-
)
240-
)
241-
views = c.fetchall()
242-
views_list = []
243-
for v in views:
244-
views_list.append(View(v))
245-
return views_list
228+
view_list = View.get_multis(profile_id=self.id)
229+
if not view_list:
230+
return []
231+
else:
232+
return view_list
246233

247234
def get_reports_received(self):
248235
logging.debug("Getting all reports received for user {}".format(self.id))
249-
with self.db.cursor() as c:
250-
c.execute(
251-
"""
252-
SELECT reports.id as id, reports.reported_id as reported_id,
253-
reports.reporter_id as reporter_id, reports.dt_reported as dt_reported,
254-
reports.details as details, reports.reason as reason, reports.status as status
255-
FROM users
256-
INNER JOIN reports on users.id = reports.reported_id
257-
WHERE users.id = CAST({} AS UNSIGNED)
258-
""".format(
259-
self.id
260-
)
261-
)
262-
reports = c.fetchall()
263-
reports_list = []
264-
for r in reports:
265-
reports_list.append(Report(r))
266-
return reports_list
236+
reports_received_list = Report.get_multis(reported_id=self.id)
237+
if not reports_received_list:
238+
return []
239+
else:
240+
return reports_received_list
267241

268242
def get_reports_sent(self):
269243
logging.debug("Getting all reports sent for user {}".format(self.id))
270-
with self.db.cursor() as c:
271-
c.execute(
272-
"""
273-
SELECT reports.id as id, reports.reported_id as reported_id,
274-
reports.reporter_id as reporter_id, reports.dt_reported as dt_reported,
275-
reports.details as details, reports.reason as reason, reports.status as status
276-
FROM users
277-
INNER JOIN reports on users.id = reports.reporter_id
278-
WHERE users.id = CAST({} AS UNSIGNED)
279-
""".format(
280-
self.id
281-
)
282-
)
283-
reports = c.fetchall()
284-
reports_list = []
285-
for r in reports:
286-
reports_list.append(Report(r))
287-
return reports_list
244+
reports_sent_list = Report.get_multis(reporter_id=self.id)
245+
if not reports_sent_list:
246+
return []
247+
else:
248+
return reports_sent_list
288249

289250
def get_likes_received(self):
290251
logging.debug("Getting all likes received for user {}".format(self.id))
291-
with self.db.cursor() as c:
292-
c.execute(
293-
"""
294-
SELECT likes.id as id, likes.liked_id as liked_id,
295-
likes.liker_id as liker_id, likes.dt_liked as dt_liked,
296-
likes.is_superlike as is_superlike
297-
FROM users
298-
INNER JOIN likes on users.id = likes.liked_id
299-
WHERE users.id = CAST({} AS UNSIGNED)
300-
""".format(
301-
self.id
302-
)
303-
)
304-
likes = c.fetchall()
305-
like_list = []
306-
for like in likes:
307-
like_list.append(Like(like))
308-
return like_list
252+
likes_received_list = Like.get_multis(liked_id=self.id)
253+
if not likes_received_list:
254+
return []
255+
else:
256+
return likes_received_list
309257

310258
def get_likes_sent(self):
311259
logging.debug("Getting all likes sent for user {}".format(self.id))
312-
with self.db.cursor() as c:
313-
c.execute(
314-
"""
315-
SELECT likes.id as id, likes.liked_id as liked_id,
316-
likes.liker_id as liker_id, likes.dt_liked as dt_liked,
317-
likes.is_superlike as is_superlike
318-
FROM users
319-
INNER JOIN likes on users.id = likes.liker_id
320-
WHERE users.id = CAST({} AS UNSIGNED)
321-
""".format(
322-
self.id
323-
)
324-
)
325-
likes = c.fetchall()
326-
like_list = []
327-
for like in likes:
328-
like_list.append(Like(like))
329-
return like_list
260+
likes_sent_list = Like.get_multis(liker_id=self.id)
261+
if not likes_sent_list:
262+
return []
263+
else:
264+
return likes_sent_list
330265

331266
def already_likes(self, liked_id: int) -> bool:
332267
with self.db.cursor() as c:

backend/PyMatcha/routes/api/debug.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from flask_jwt_extended import current_user
88
from flask_jwt_extended import jwt_required
99
from PyMatcha import redis
10+
from PyMatcha.models.image import Image
1011
from PyMatcha.models.like import Like
1112
from PyMatcha.models.match import Match
1213
from PyMatcha.models.message import Message
@@ -141,6 +142,7 @@ def delete_tables():
141142
User.drop_table()
142143
Tag.drop_table()
143144
Message.drop_table()
145+
Image.drop_table()
144146

145147
Match.create_table()
146148
Like.create_table()
@@ -149,6 +151,7 @@ def delete_tables():
149151
User.create_table()
150152
Tag.create_table()
151153
Message.create_table()
154+
Image.create_table()
152155
return "", 204
153156

154157

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
from io import BytesIO
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.models.image import Image
8+
from PyMatcha.utils.errors import BadRequestError
9+
from PyMatcha.utils.errors import NotFoundError
10+
from PyMatcha.utils.images import upload_image
11+
from PyMatcha.utils.success import Success
12+
from PyMatcha.utils.success import SuccessDeleted
13+
from PyMatcha.utils.success import SuccessOutput
14+
15+
images_bp = Blueprint("images", __name__)
16+
17+
18+
@images_bp.route("/profile/images", methods=["POST"])
19+
@jwt_required
20+
def add_image_profile():
21+
is_primary = request.args.get("is_primary", "false") == "true"
22+
# check if the post request has the file part
23+
if "file[]" not in request.files:
24+
raise BadRequestError("No file body passed in request form data")
25+
file = request.files["file[]"]
26+
# if user does not select file, browser also
27+
# submit an empty part without filename
28+
if file.filename == "":
29+
raise BadRequestError("No file passed in request")
30+
if file:
31+
if is_primary:
32+
try:
33+
Image.get_multi(user_id=current_user.id, is_primary=True)
34+
except ValueError:
35+
# That means there was no primary image before
36+
tmp_img = BytesIO()
37+
file.save(tmp_img)
38+
try:
39+
link = upload_image(tmp_img, current_user.username)
40+
except BadRequestError as e:
41+
raise e
42+
Image.create(current_user.id, link, is_primary=True)
43+
return SuccessOutput("image", link)
44+
else:
45+
raise BadRequestError("There already is a primary image for user {}".format(current_user.id))
46+
else:
47+
images = Image.get_multis(user_id=current_user.id, is_primary=False)
48+
if images:
49+
image_count = len(Image.get_multis(user_id=current_user.id, is_primary=False))
50+
else:
51+
image_count = 0
52+
if image_count >= 4:
53+
raise BadRequestError("There's already enough images for this account")
54+
tmp_img = BytesIO()
55+
file.save(tmp_img)
56+
try:
57+
link = upload_image(tmp_img, current_user.username)
58+
except BadRequestError as e:
59+
raise e
60+
Image.create(current_user.id, link, is_primary=False)
61+
return SuccessOutput("image", link)
62+
else:
63+
raise ValueError("NO FILE")
64+
65+
66+
@images_bp.route("/profile/images/<image_id>", methods=["DELETE"])
67+
@jwt_required
68+
def delete_image_profile(image_id):
69+
try:
70+
image = Image.get(id=image_id)
71+
except ValueError:
72+
raise NotFoundError(f"Image not found for user {current_user.id}")
73+
image.delete()
74+
return SuccessDeleted("Image successfully deleted.")
75+
76+
77+
@images_bp.route("/profile/images/<image_id>", methods=["PUT"])
78+
@jwt_required
79+
def change_main_image(image_id):
80+
try:
81+
image = Image.get(id=image_id)
82+
except ValueError:
83+
raise NotFoundError(f"Image not found for user {current_user.id}")
84+
try:
85+
current_main_image = Image.get_multi(user_id=current_user.id, is_primary=True)
86+
except NotFoundError:
87+
# That means there was no primary image before
88+
pass
89+
else:
90+
current_main_image.is_primary = False
91+
current_main_image.save()
92+
image.is_primary = True
93+
image.save()
94+
return Success("Profile picture successfully modified")
95+
96+
97+
@images_bp.route("/profile/images", methods=["GET"])
98+
@jwt_required
99+
def get_images_profile():
100+
images = Image.get_multis(user_id=current_user.id)
101+
ret = []
102+
if images:
103+
ret = [image.to_dict() for image in images]
104+
return SuccessOutput("images", ret)

0 commit comments

Comments
 (0)