Skip to content
Open
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
74 commits
Select commit Hold shift + click to select a range
fef70ba
feature: add favorite quotes
subscorp Feb 15, 2021
da92003
fix: fixed conflicts
subscorp Feb 15, 2021
4d4ac98
fix: fixed flake8 error
subscorp Feb 15, 2021
9a103e6
Merge branch 'develop' of https://github.com/PythonFreeCourse/calenda…
subscorp Feb 15, 2021
8392bb2
fix: fixed pytest issues
subscorp Feb 15, 2021
c728f0c
a small fix
subscorp Feb 15, 2021
f62f8d3
fix: fixed display issue in favorite_quotes.html
subscorp Feb 15, 2021
82d79fd
fix: moved favorite quotes from navigation bar to profile
subscorp Feb 15, 2021
26f6a91
fix: fixed issues according to comments
subscorp Feb 16, 2021
076ab07
fix: fixed conflicts
subscorp Feb 17, 2021
701228e
fix: fixed issues according to comments
subscorp Feb 18, 2021
a9fe632
fix: fixed conflicts
subscorp Feb 18, 2021
fbe286c
fix: fixed flake8 issues
subscorp Feb 18, 2021
3026dd7
fix: fixed issue in pytest
subscorp Feb 18, 2021
2238a8d
fix: a tiny improvement
subscorp Feb 18, 2021
d8ce7b5
fix: small fix
subscorp Feb 18, 2021
47b0b5f
fix: another tiny improvement
subscorp Feb 18, 2021
1308754
fix: fixed issues according to comments
subscorp Feb 20, 2021
fef81a7
fix: fixed conflicts
subscorp Feb 20, 2021
e53fab9
fix: fixed pytest issues
subscorp Feb 20, 2021
fedc394
fix: a small fix
subscorp Feb 20, 2021
ee7ec43
fix: another small fix
subscorp Feb 20, 2021
c012d64
Update: now using the new login system
subscorp Feb 20, 2021
ac4a818
Update: now using the new login system
subscorp Feb 20, 2021
1800655
fix: fixed low coverage in pytest
subscorp Feb 20, 2021
a975a1c
fix: moved styles to css
subscorp Feb 21, 2021
bdba51f
fix: fixed conflicts
subscorp Feb 21, 2021
34d1ee6
Update: moved Favorite Quotes to profile
subscorp Feb 21, 2021
3098862
fix: fixed issues according to comments
subscorp Feb 22, 2021
1791fa6
fix: fixed conflicts
subscorp Feb 22, 2021
b722eea
fix: fixed pytest issue
subscorp Feb 22, 2021
5e5f6c3
fix: fixed issues in pytest
subscorp Feb 22, 2021
5d243e3
fix: small fix
subscorp Feb 22, 2021
56b0988
fix: fixed conflicts
subscorp Feb 23, 2021
097271a
fix: fixed issues according to comments
subscorp Feb 23, 2021
667b4a3
Merge branch 'develop' of https://github.com/PythonFreeCourse/calenda…
subscorp Feb 23, 2021
8a0464e
fix: a small fix
subscorp Feb 23, 2021
1fa587a
fix: fixed issues in pytest
subscorp Feb 23, 2021
de61e04
fix: flake8 error fix
subscorp Feb 24, 2021
f6355dc
fix: flake8 error fix
subscorp Feb 24, 2021
9c3a795
fix: flake8 error fix
subscorp Feb 24, 2021
f2c7d7a
fix: fixed conflicts
subscorp Feb 24, 2021
0e0f704
fix: revert some changes due to a bug
subscorp Feb 24, 2021
6bb30e1
Update: moved functions out of main
subscorp Feb 24, 2021
0a846f7
fix: a small fix
subscorp Feb 24, 2021
a059d81
Updates: A. heart now appears only when logged in. B. landing page no…
subscorp Feb 24, 2021
9a76d3f
fix: some small fixes and mainly added tests to both the audio featur…
subscorp Feb 25, 2021
37ec594
fix: fixed conflicts
subscorp Feb 25, 2021
0ea5847
fix: fixed issues according to comments
subscorp Feb 25, 2021
9f55f15
fix: fixed issues according to comments
subscorp Feb 25, 2021
99f5e33
fix: a small, final fix
subscorp Feb 25, 2021
c390fea
fix: fixed issues according to comments
subscorp Feb 25, 2021
3e33aa2
fix: fixed merge issue :)
subscorp Feb 26, 2021
79a8e6d
fix: fixed conflicts
subscorp Feb 26, 2021
19aae78
fix: trying to fix a bug in pytest
subscorp Feb 26, 2021
c671d5f
fix: trying to fix the bug
subscorp Feb 26, 2021
1e4c089
fix: trying to fix the bug
subscorp Feb 26, 2021
dccf688
fix: trying to fix the bug
subscorp Feb 26, 2021
095fcea
Merge branch 'develop' of https://github.com/PythonFreeCourse/calenda…
subscorp Feb 26, 2021
60af145
trying to fix the bug
subscorp Feb 26, 2021
07f6cc6
fix: trying to fix the bug (it's only in github)
subscorp Feb 26, 2021
970f296
fix: trying to fix the bug (it's only in github)
subscorp Feb 26, 2021
e5bcdc5
fix: trying to fix the bug
subscorp Feb 26, 2021
0504e22
fix: trying to fix the bug
subscorp Feb 26, 2021
12481b4
fix: trying to fix the bug
subscorp Feb 26, 2021
0d4042f
fix: trying to fix the bug
subscorp Feb 26, 2021
de05d1a
fix: trying to fix the bug
subscorp Feb 26, 2021
4abe14d
fix: trying to fix the bug
subscorp Feb 26, 2021
404e195
fix: trying to fix the bug
subscorp Feb 26, 2021
23becd6
fix: trying to fix the bug
subscorp Feb 26, 2021
943162a
fix: trying to fix the bug
subscorp Feb 26, 2021
9f5244e
fix: trying to fix the bug
subscorp Feb 26, 2021
cac2432
Merge branch 'develop' of https://github.com/PythonFreeCourse/calenda…
subscorp Feb 26, 2021
5fd6929
fix: fixed issues according to comments
subscorp Feb 26, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions app/database/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,7 @@ class Quote(Base):
id = Column(Integer, primary_key=True, index=True)
text = Column(String, nullable=False)
author = Column(String)
is_favorite = Column(Boolean)


class Comment(Base):
Expand Down Expand Up @@ -344,3 +345,11 @@ def insert_data(target, session: Session, **kw):


event.listen(Language.__table__, 'after_create', insert_data)


class UserQuotes(Base):
__tablename__ = "user_quotes"

id = Column(Integer, primary_key=True, index=True)
user_id = Column(Integer, ForeignKey("users.id"), nullable=False)
quote_id = Column(Integer, ForeignKey("quotes.id"), nullable=False)
22 changes: 19 additions & 3 deletions app/internal/daily_quotes.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
from datetime import date
from typing import Dict, Optional

from app.database.models import Quote, UserQuotes
from sqlalchemy.orm import Session
from sqlalchemy.sql.expression import func

from app.database.models import Quote

TOTAL_DAYS = 366


Expand All @@ -14,7 +13,8 @@ def create_quote_object(quotes_fields: Dict[str, Optional[str]]) -> Quote:
It is used for adding the data from the json into the db"""
return Quote(
text=quotes_fields['text'],
author=quotes_fields['author']
author=quotes_fields['author'],
is_favorite=False
)


Expand All @@ -28,3 +28,19 @@ def quote_per_day(
quote = session.query(Quote).filter(
Quote.id % TOTAL_DAYS == day_num).order_by(func.random()).first()
return quote


def get_quotes(session, user_id):
"""Retrieves the users' favorite quotes from the database."""
quotes = []
user_quotes = session.query(UserQuotes).filter_by(user_id=user_id).all()
for user_quote in user_quotes:
quote = session.query(Quote).filter_by(id=user_quote.quote_id).first()
quote.is_favorite = True
quotes.append(quote)
return quotes


def get_quote_id(session, quote):
"""Retrieve quote id given the text of the quote."""
return session.query(Quote).filter_by(text=quote).first().id
67 changes: 53 additions & 14 deletions app/main.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,19 @@
from fastapi import Depends, FastAPI, Form, Request
from fastapi.openapi.docs import (get_swagger_ui_html,
get_swagger_ui_oauth2_redirect_html)
from fastapi.staticfiles import StaticFiles
from sqlalchemy.orm import Session
from starlette.status import HTTP_401_UNAUTHORIZED

from app import config
from app.database import engine, models
from app.dependencies import get_db, logger, MEDIA_PATH, STATIC_PATH, templates
from app.database.models import UserQuotes
from app.dependencies import MEDIA_PATH, STATIC_PATH, get_db, logger, templates
from app.internal import daily_quotes, json_data_loader

from app.internal.daily_quotes import get_quote_id
from app.internal.languages import set_ui_language
from app.internal.security.ouath2 import auth_exception_handler
from app.utils.extending_openapi import custom_openapi
from app.routers.salary import routes as salary
from fastapi import Depends, FastAPI, Request
from fastapi.openapi.docs import (
get_swagger_ui_html,
get_swagger_ui_oauth2_redirect_html,
)
from fastapi.staticfiles import StaticFiles
from starlette.status import HTTP_401_UNAUTHORIZED
from sqlalchemy.orm import Session


def create_tables(engine, psql_environment):
Expand Down Expand Up @@ -99,16 +98,56 @@ async def swagger_ui_redirect():
app.include_router(router)


# TODO: I add the quote day to the home page
# until the relevant calendar view will be developed.
@app.get("/", include_in_schema=False)
@logger.catch()
async def home(request: Request, db: Session = Depends(get_db)):
"""Home page for the website."""
quote = daily_quotes.quote_per_day(db)
user_quotes = daily_quotes.get_quotes(db, 1)
for user_quote in user_quotes:
if user_quote.id == quote.id:
quote.is_favorite = True
return templates.TemplateResponse("index.html", {
"request": request,
"quote": quote,
})


custom_openapi(app)
@app.post("/")
async def save_or_delete_quote(
user_id: int = Form(...),
quote: str = Form(...),
db: Session = Depends(get_db)):
"""Saves a quote in the database."""
quote_id = get_quote_id(db, quote)
record = db.query(UserQuotes).filter(
UserQuotes.user_id == user_id, UserQuotes.quote_id == quote_id).first()
if not record:
db.add(UserQuotes(user_id=user_id, quote_id=quote_id))
db.commit()


@app.delete("/")
async def delete_quote(
user_id: int = Form(...),
quote: str = Form(...),
db: Session = Depends(get_db)):
"""Deletes a quote from the database."""
quote_id = get_quote_id(db, quote)
db.query(UserQuotes).filter(
UserQuotes.user_id == user_id,
UserQuotes.quote_id == quote_id).delete()
db.commit()


@app.get("/favorite_quotes")
async def favorite_quotes(
request: Request,
db: Session = Depends(get_db),
user_id: int = 1):
"""html page for displaying the users' favorite quotes."""
quotes = daily_quotes.get_quotes(db, user_id)
return templates.TemplateResponse("favorite_quotes.html", {
"request": request,
"quotes": quotes,
})
Binary file added app/media/empty_heart.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added app/media/full_heart.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
48 changes: 48 additions & 0 deletions app/static/favorite_quotes.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// Adding event listeners
let hearts = Array.from(document.getElementsByClassName("heart"));
hearts.forEach((heart) => {
if (heart) {
heart.addEventListener("click", on_heart_click);
}
});

/**
* @summary This function is a handler for the event of heart-click.
* Whenever a user clicks on a heart icon, in case of empty heart:
* saves quote in favorites, as well as changing
* the heart icon from empty to full.
* In case of full heart:
* Removes it and switch back to empty heart icon.
* Uses the save_or_remove_quote function to handle db operations.
*/
function on_heart_click() {
const quote = this.parentNode.previousElementSibling.innerHTML.replaceAll(
'"',
""
);
const author_element = this.parentNode;
const author = author_element.innerHTML.split("\\ ")[1].split("\n")[0];
if (this.src.split("/").pop() == "empty_heart.png") {
this.src = "../media/full_heart.png";
save_or_remove_quote(1, quote, true);
} else {
this.src = "../media/empty_heart.png";
save_or_remove_quote(1, quote, false);
if (this.classList.contains("favorites")) {
this.parentNode.parentNode.remove();
}
}
}

/**
* @summary Saves or removes a quote from favorites.
*/
function save_or_remove_quote(user_id, quote, to_save) {
method = to_save ? "post": "delete";
let xhr = new XMLHttpRequest();
xhr.open(method, "/");
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
xhr.send(
`user_id=${user_id}&quote=${quote}`
);
}
18 changes: 18 additions & 0 deletions app/static/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@ p {
justify-content: space-around;
}

p#p_quote {
align-items: center;
justify-content: center;
}

#inner:hover {
cursor: pointer;
padding: 50px;
Expand Down Expand Up @@ -97,6 +102,19 @@ p {
background-color: whitesmoke;
}

.subtitle {
font-size: 20px;
}

.heart {
width: 1.5rem;
height: 1.5rem;
}

#favorite-quotes {
margin-bottom: 2rem;
}

.error-message {
line-height: 0;
color: red;
Expand Down
4 changes: 4 additions & 0 deletions app/templates/base.html
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@
<li class="nav-item">
<a class="nav-link" href="{{ url_for('friendview') }}">Friend View</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{{ url_for('favorite_quotes') }}">Favorite Quotes</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{{ url_for('about') }}">{{ gettext("About Us") }}</a>
</li>
Expand All @@ -75,6 +78,7 @@
<!-- This bootstrap version is needed here because of the toggler bug in the beta version-->
<script src="https://stackpath.bootstrapcdn.com/bootstrap/5.0.0-alpha2/js/bootstrap.bundle.min.js" integrity="sha384-BOsAfwzjNJHrJ8cZidOg56tcQWfp6y72vEJ8xQ9w6Quywb24iOsW913URv1IS4GD" crossorigin="anonymous"></script>
<script src="{{ url_for('static', path='/horoscope.js') }}"></script>
<script src="{{ url_for('static', path='/favorite_quotes.js') }}"></script>
</body>

</html>
23 changes: 23 additions & 0 deletions app/templates/favorite_quotes.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{% extends "base.html" %}

{% block content %}
<div class="d-flex position-relative">
<div class="container mx-auto gap-3">
<h2 id="favorite-quotes">Favorite Quotes</h2>
{% for quote in quotes %}
{% if not quote.author %}
<p><i>"{{ quote.text }}"</i></p>
{% else %}
<p id="p_quote"><i id="quote">"{{ quote.text }}"</i> <span id="author" style="font-size: small;">&nbsp; \ {{ quote.author }}
{% if quote.is_favorite %}
<img src="../media/full_heart.png" class="heart favorites">
{% else %}
<img src="../media/empty_heart.png" class="heart favorites">
{% endif %}
</span>
</p>
{% endif %}
{% endfor %}
</div>
</div>
{% endblock %}
26 changes: 8 additions & 18 deletions app/templates/home.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,31 +2,21 @@

{% block content %}


<div class="container mt-4">
</div>


<!-- I chose to add the quote day to the home page until the relavent calendar view will be developed. -->
<div>
{% if quote %}
{% if not quote.author %}
<p><i>"{{ quote.text }}"</i></p>
{% else %}
<p><i>"{{ quote.text }}"</i> <span style="font-size: small;">&nbsp; \ {{ quote.author }}</span></p>
{% endif %}
{% endif %}
</div>

<!-- I chose to add the quote day to the home page until the relavent calendar view will be developed. -->
<div>
{% if quote %}
{% if not quote.author%}
<p><i>"{{ quote.text }}"</i></p>
<p><i id="quote">"{{ quote.text }}"</i> <span id="author" style="font-size: small;">&nbsp; \ {{ quote.author }}
{% if quote.is_favorite %}
<img src="../media/full_heart.png" class="heart">
{% else %}
<p><i>"{{ quote.text }}"</i> <span style="font-size: small;">&nbsp; \ {{quote.author}}</span></p>
<img src="../media/empty_heart.png" class="heart">
{% endif %}
</span>
</p>
{% endif %}
{% endif %}
</div>
</div>

{% endblock %}
15 changes: 10 additions & 5 deletions app/templates/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,20 @@ <h2 class="fs-1 fw-bold">
</p>
<div class="d-flex position-relative">
<div class="container mx-auto gap-3">

{% if quote %}
{% if not quote.author%}
<p><i>"{{ quote.text }}"</i></p>
{% if not quote.author %}
<p><i>"{{ quote.text }}"</i></p>
{% else %}
<p><i>"{{ quote.text }}"</i> <span style="font-size: small;">&nbsp; \ {{quote.author}}</span>
<p><i id="quote">"{{ quote.text }}"</i> <span id="author" style="font-size: small;">&nbsp; \ {{ quote.author }}
{% if quote.is_favorite %}
<img src="../media/full_heart.png" class="heart">
{% else %}
<img src="../media/empty_heart.png" class="heart">
{% endif %}
</span>
</p>
{% endif %}
{% endif %}
{% endif %}
</div>
</div>
<div class="d-flex justify-content-center">
Expand Down
1 change: 1 addition & 0 deletions app/templates/partials/base.html
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
integrity="sha512-d9xgZrVZpmmQlfonhQUvTR7lMPtO7NkZMkA0ABN3PHCbKA5nqylQ/yWlFAyY6hYgdF1Qh6nYiuADWwKB4C2WSw=="
crossorigin="anonymous"></script>
<script defer type="text/javascript" src="{{ url_for('static', path='/popover.js') }}"></script>
<script defer type="text/javascript" src="{{ url_for('static', path='/favorite_quotes.js') }}"></script>
{% endblock head %}
{% block title %}
<title>Pylendar{% if self.page_name() %} - {% endif %}{% block page_name %}{% endblock %}</title>
Expand Down
3 changes: 3 additions & 0 deletions app/templates/partials/index/navigation.html
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@
<li class="nav-item">
<a class="nav-link" href="/search">Search</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{{ url_for('favorite_quotes') }}">Favorite Quotes</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{{ url_for('about') }}">{{ gettext("About Us") }}</a>
</li>
Expand Down
3 changes: 3 additions & 0 deletions app/templates/profile.html
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,9 @@ <h6 class="card-title text-center mb-1">{{ user.full_name }}</h6>
<li class="list-group-item list-group-item-action no-border">
<a class="text-decoration-none text-secondary" href="{{ url_for('import_holidays') }}">Add holidays to calendar</a>
</li>
<li class="list-group-item list-group-item-action no-border">
<a class="text-decoration-none text-secondary" href="{{ url_for('favorite_quotes') }}">Favorite Quotes</a>
</li>
<li class="list-group-item list-group-item-action no-border">
{% if google_error %}
<a class="text-decoration-none text-secondary" href="{{ url_for('google_sync') }}">Sync with Google - Not
Expand Down
5 changes: 5 additions & 0 deletions tests/client_fixture.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,11 @@ def profile_test_client() -> Iterator[TestClient]:
Base.metadata.drop_all(bind=test_engine)


@pytest.fixture(scope="session")
def settings_test_client() -> Iterator[TestClient]:
yield from create_test_client(profile.get_db)


@pytest.fixture(scope="session")
def security_test_client():
yield from create_test_client(event.get_db)
Expand Down
Loading