Skip to content

Feature/cursor #307

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 38 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
8bbca9c
feature: add cursor settings
subscorp Feb 16, 2021
056adc5
fix: fixed conflicts
subscorp Feb 16, 2021
914a808
fix: fixed pytest issue
subscorp Feb 16, 2021
05660da
fix: fixed conflicts
subscorp Feb 16, 2021
8312440
update: changed CursorSettings table to UserSettings
subscorp Feb 17, 2021
0db4d2b
fix:solved conflicts
subscorp Feb 17, 2021
53de34a
fix: fixed a small bug
subscorp Feb 17, 2021
6dd0219
fix: another small fix
subscorp Feb 17, 2021
fc72d95
fix: fixed issues according to comments
subscorp Feb 18, 2021
25ca2d3
fix: fixed conflicts
subscorp Feb 18, 2021
4d2195e
Update: now using the new login system
subscorp Feb 21, 2021
2114a33
fix: fixed conflicts
subscorp Feb 21, 2021
4ce2c74
fix: fixed issues according to comments
subscorp Feb 21, 2021
4f06799
fix: fixed issues according to comments
subscorp Feb 23, 2021
f569f3c
fix: fixed conflicts
subscorp Feb 23, 2021
e289578
Update: moved functions to internals
subscorp Feb 23, 2021
44da8b7
fix: fixed issue in pytest
subscorp Feb 23, 2021
7b616ec
Merge branch 'develop' of https://github.com/PythonFreeCourse/calenda…
subscorp Feb 23, 2021
8b0cd21
fix: a small fix
subscorp Feb 23, 2021
dbae35d
fix: fixed issues according to comments
subscorp Feb 25, 2021
03e21d1
fix: fixed conflicts
subscorp Feb 25, 2021
458fb3f
fix: fixed a bug in pytest
subscorp Feb 25, 2021
df699f6
fix: fixed a small bug in one of the tests. now coverage is 100% on t…
subscorp Feb 25, 2021
c847b41
fix: fixed issues according to comments
subscorp Feb 25, 2021
479f580
fix: fixed conflicts
subscorp Feb 25, 2021
b01c459
fix: a tiny fix
subscorp Feb 25, 2021
969bcb1
fix: fixed finale comment
subscorp Feb 26, 2021
d0336bf
Merge branch 'develop' of https://github.com/PythonFreeCourse/calenda…
subscorp Feb 26, 2021
01456c8
fix: trying to fix the same bug from favorite_quotes in pytest
subscorp Feb 26, 2021
5b54d04
fix: fixed issue in pytest
subscorp Feb 26, 2021
0fdce31
fix: trying to fix the bug
subscorp Feb 26, 2021
7dcfc02
fix: trying to fix the bug
subscorp Feb 26, 2021
021881b
fix: trying to fix the bug
subscorp Feb 26, 2021
8e057c5
fix: trying to fix the bug
subscorp Feb 26, 2021
2ec4a77
fix: trying to fix the bug
subscorp Feb 26, 2021
d050246
fix: trying to fix the bug
subscorp Feb 26, 2021
a1a9f66
fix: fixed the bug!
subscorp Feb 26, 2021
6d2bcf5
fix: fixed conflicts
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
1 change: 1 addition & 0 deletions app/dependencies.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
MEDIA_PATH = os.path.join(APP_PATH, config.MEDIA_DIRECTORY)
STATIC_PATH = os.path.join(APP_PATH, "static")
TEMPLATES_PATH = os.path.join(APP_PATH, "templates")
CURSORS_PATH = os.path.join(MEDIA_PATH, "cursors")
SOUNDS_PATH = os.path.join(STATIC_PATH, "tracks")
templates = Jinja2Templates(directory=TEMPLATES_PATH)
templates.env.add_extension("jinja2.ext.i18n")
Expand Down
54 changes: 54 additions & 0 deletions app/internal/cursor.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
from typing import List, Optional, Tuple

from sqlalchemy.orm.session import Session

from app.database.models import User, UserSettings


def get_cursor_settings(
session: Session,
user_id: int,
) -> Tuple[Optional[List[str]], Optional[int], Optional[str], Optional[int]]:
"""Retrieves cursor settings from the database.

Args:
session (Session): the database.
user_id (int, optional): the users' id.

Returns:
Tuple[str, Optional[List[str]], Optional[int],
str, Optional[str], Optional[int]]: the cursor settings.
"""
primary_cursor, secondary_cursor = None, None
cursor_settings = (
session.query(UserSettings).filter_by(user_id=user_id).first()
)
if cursor_settings:
primary_cursor = cursor_settings.primary_cursor
secondary_cursor = cursor_settings.secondary_cursor

return primary_cursor, secondary_cursor


def save_cursor_settings(
session: Session,
user: User,
cursor_choices: List[str],
):
"""Saves cursor choices in the db.

Args:
session (Session): the database.
user (User): current user.
cursor_choices (List[str]): primary and secondary cursors.
"""
cursor_settings = (
session.query(UserSettings).filter_by(user_id=user.user_id).first()
)
if cursor_settings:
session.query(UserSettings).filter_by(
user_id=cursor_settings.user_id,
).update(cursor_choices)
else:
session.merge(UserSettings(user_id=user.user_id, **cursor_choices))
session.commit()
6 changes: 4 additions & 2 deletions app/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,20 @@
from fastapi.staticfiles import StaticFiles
from sqlalchemy.orm import Session

import app.internal.features as internal_features
from app import config
from app.database import engine, models
from app.dependencies import (
MEDIA_PATH,
SOUNDS_PATH,
STATIC_PATH,
UPLOAD_PATH,
SessionLocal,
get_db,
logger,
templates,
SessionLocal,
)
from app.internal import daily_quotes, json_data_loader
import app.internal.features as internal_features
from app.internal.languages import set_ui_language
from app.internal.security.ouath2 import auth_exception_handler
from app.routers.salary import routes as salary
Expand Down Expand Up @@ -64,6 +64,7 @@ def create_tables(engine, psql_environment):
celebrity,
credits,
currency,
cursor,
dayview,
email,
event,
Expand Down Expand Up @@ -118,6 +119,7 @@ async def swagger_ui_redirect():
celebrity.router,
credits.router,
currency.router,
cursor.router,
dayview.router,
email.router,
event.router,
Expand Down
Binary file added app/media/cursors/Link.cur
Binary file not shown.
Binary file added app/media/cursors/Link2.cur
Binary file not shown.
Binary file added app/media/cursors/Material_3d.cur
Binary file not shown.
Binary file added app/media/cursors/Valentine_Heart.cur
Binary file not shown.
Binary file added app/media/cursors/Wand.cur
Binary file not shown.
Binary file added app/media/cursors/among_us.cur
Binary file not shown.
Binary file added app/media/cursors/blue_cursor.cur
Binary file not shown.
Binary file added app/media/cursors/blue_finger.cur
Binary file not shown.
13 changes: 13 additions & 0 deletions app/media/cursors/credits.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
http://www.rw-designer.com/cursor-set/material-amber
http://www.rw-designer.com/cursor-set/windows-xp-style
http://www.rw-designer.com/cursor-set/material-3d
http://www.rw-designer.com/cursor-set/paper-plane
http://www.rw-designer.com/cursor-set/yodalighsaber
http://www.rw-designer.com/cursor-set/valentine-heart
http://www.rw-designer.com/cursor-set/icy-ice
http://www.rw-designer.com/cursor-set/wii-hand-1
http://www.rw-designer.com/cursor-set/pen-tablet-1
http://www.rw-designer.com/cursor-set/among-us-mixed-pointer-pack-
http://www.rw-designer.com/cursor-set/nes-smb-mario
http://www.rw-designer.com/cursor-set/mario-world-v64
http://www.rw-designer.com/cursor-set/the-legend-of-zelda
Binary file added app/media/cursors/fire.cur
Binary file not shown.
Binary file added app/media/cursors/green_cursor.cur
Binary file not shown.
Binary file added app/media/cursors/green_finger.cur
Binary file not shown.
Binary file added app/media/cursors/green_lightsaber.cur
Binary file not shown.
Binary file added app/media/cursors/ice.cur
Binary file not shown.
Binary file added app/media/cursors/lime_cursor.cur
Binary file not shown.
Binary file added app/media/cursors/nes_mario.cur
Binary file not shown.
Binary file added app/media/cursors/painted_arrow.cur
Binary file not shown.
Binary file added app/media/cursors/painted_finger.cur
Binary file not shown.
Binary file added app/media/cursors/paper-plane.cur
Binary file not shown.
Binary file added app/media/cursors/pen.cur
Binary file not shown.
Binary file added app/media/cursors/pink_cursor.cur
Binary file not shown.
Binary file added app/media/cursors/red_cursor.cur
Binary file not shown.
Binary file added app/media/cursors/red_finger.cur
Binary file not shown.
Binary file added app/media/cursors/snes_mario.cur
Binary file not shown.
Binary file added app/media/cursors/sword.cur
Binary file not shown.
Binary file added app/media/cursors/xp_arrow.cur
Binary file not shown.
Binary file added app/media/cursors/yellow_cursor.cur
Binary file not shown.
Binary file added app/media/cursors/yellow_finger.cur
Binary file not shown.
103 changes: 103 additions & 0 deletions app/routers/cursor.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import json
from pathlib import Path

from fastapi import APIRouter, Depends, Form, Request
from sqlalchemy.orm.session import Session
from starlette.responses import RedirectResponse
from starlette.status import HTTP_302_FOUND

from app.database.models import User
from app.dependencies import CURSORS_PATH, get_db, templates
from app.internal.cursor import get_cursor_settings, save_cursor_settings
from app.internal.security.dependencies import current_user

router = APIRouter(
prefix="/cursor",
tags=["cursor"],
Comment on lines +14 to +16
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm not sure we need a route for every setting option. This should probably be routers.setting.py and the calls should be:

  • @router.get("/settings/cursor")
  • @router.post("/settings/cursor")
  • @router.get("/{user_id}/cursor")

Copy link
Author

Choose a reason for hiding this comment

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

Did you mean that there shouldn't be a [setting_type].py file for each setting type?
So if I have Audio Settings and Cursor Settings, both of them should be in routers.setting.py, but the @router functions should stay the same, only in this new file instead of the old one?
Thanks for your feedback!

responses={404: {"description": "Not found"}},
)


@router.get("/settings")
def cursor_settings(
request: Request,
user: User = Depends(current_user),
session: Session = Depends(get_db),
) -> templates.TemplateResponse:
"""A route to the cursor settings.

Args:
request (Request): the http request.
session (Session): the database.

Returns:
templates.TemplateResponse: renders the cursor_settings.html page
with the relevant information.
"""
cursors = ["default"] + [
path.stem for path in Path(CURSORS_PATH).glob("**/*.cur")
]

return templates.TemplateResponse(
"cursor_settings.html",
{
"request": request,
"cursors": cursors,
},
)


@router.post("/settings")
async def get_cursor_choices(
session: Session = Depends(get_db),
user: User = Depends(current_user),
primary_cursor: str = Form(...),
secondary_cursor: str = Form(...),
) -> RedirectResponse:
"""The form in which the user choses primary and secondary
cursors.

Args:
session (Session, optional): the database.
user (User, optional): [description]. temp user.
primary_cursor (str, optional): name of the primary cursor.
the primary cursor.
secondary_cursor (str, optional): name of the secondary cursor.

Returns:
RedirectResponse: redirects to the homepage.
"""
cursor_choices = {
"primary_cursor": primary_cursor,
"secondary_cursor": secondary_cursor,
}
save_cursor_settings(session, user, cursor_choices)

return RedirectResponse("/", status_code=HTTP_302_FOUND)


@router.get("/load")
async def load_cursor(
session: Session = Depends(get_db),
user: User = Depends(current_user),
) -> RedirectResponse:
"""loads cursors according to cursor settings.

Args:
session (Session): the database.
user (User): the user.

Returns:
RedirectResponse: redirect the user to the homepage.
"""
primary_cursor, secondary_cursor = get_cursor_settings(
session,
user.user_id,
)

return json.dumps(
{
"primary_cursor": primary_cursor,
"secondary_cursor": secondary_cursor,
},
)
87 changes: 87 additions & 0 deletions app/static/cursor.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
const CURSORS_PATH = "/media/cursors/";

// These must be global so the mutationObserver could access them.
let primary_cursor;
let secondary_cursor;

window.addEventListener("load", init);

/**
* @summary In charge of initialising the customization of the cursor.
*/
function init() {
get_cursor_choices();
initMutationObserver();
}

/**
* @summary This function gets cursor choices from the db.
*/
function get_cursor_choices() {
const request = new XMLHttpRequest();
request.open("GET", "/cursor/load", true);
request.onload = change_cursor;
request.send();
}

/**
* @summary This function changes the primary cursor and the secondary
* cursor according to users' choices.
*/
function change_cursor() {
const cursor_settings = JSON.parse(JSON.parse(this.response));
const primary_cursor_choice = cursor_settings["primary_cursor"];
const primary_cursor_path = `url(${CURSORS_PATH}${primary_cursor_choice}), auto`;
const secondary_cursor_choice = cursor_settings["secondary_cursor"];
const secondary_cursor_path = `url(${CURSORS_PATH}${secondary_cursor_choice}), auto`;
primary_cursor =
primary_cursor_choice !== "default.cur" ? primary_cursor_path : "";
secondary_cursor =
secondary_cursor_choice !== "default.cur" ? secondary_cursor_path : "";
document.body.style.cursor = primary_cursor;
const links = document.querySelectorAll("a, button, input, select, label");
links.forEach((element) => {
element.style.cursor = secondary_cursor;
});
}

/**
* @summary Sets up mutation observer to follow dynamically added links
*/
function initMutationObserver() {
const config = {
childList: true,
subtree: true,
};
const observer = new MutationObserver(mutate);
observer.observe(document, config);
}

/**
* @summary This function identifies a new element for the secondary cursor,
* and sets it according to the users' choices.
*/
function mutate(mutationList) {
const links = ["a", "button", "input", "select", "label"];
for (let mutation of mutationList) {
if (mutation.type == "childList") {
handle_potential_links(mutation.addedNodes, links);
}
}
}

/**
* @summary Helper function to the mutate function which
* on creation of new nodes in the DOM, if it is a link - changes its'
* style.
*/
function handle_potential_links(nodes, links) {
nodes.forEach((element) => {
if (
typeof(element.tagName) !== "undefined" &&
links.includes(element.tagName.toLowerCase())
) {
element.setAttribute("style", secondary_cursor);
}
});
}
7 changes: 7 additions & 0 deletions app/static/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,13 @@ h2.modal-title {
font-size: 1.25rem;
}

#primary-cursor,
#secondary-cursor {
width: 10em;
height: 2.5em;
margin-top: 0.5em;
}

#sfx {
width: 5rem;
height: 2.5rem;
Expand Down
6 changes: 6 additions & 0 deletions app/templates/base.html
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,9 @@
<li class="nav-item">
<a class="nav-link" href="{{ url_for('weight') }}">Weight</a>
</li>
</li>
<a class="nav-link" href="{{ url_for('cursor_settings') }}">Cursor Settings</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{{ url_for('about') }}">{{ gettext("About Us") }}</a>
</li>
Expand Down Expand Up @@ -91,6 +94,9 @@
<script defer src="https://use.fontawesome.com/releases/v5.0.8/js/all.js" integrity="sha384-SlE991lGASHoBfWbelyBPLsUlwY1GwNDJo3jSJO04KZ33K2bwfV9YBauFfnzvynJ" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js" integrity="sha384-ygbV9kiqUc6oa4msXn9868pTtWMgiQaeYH7/t7LECLbyPA2x65Kgf80OJFdroafW" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.4/Chart.min.js" integrity="sha512-d9xgZrVZpmmQlfonhQUvTR7lMPtO7NkZMkA0ABN3PHCbKA5nqylQ/yWlFAyY6hYgdF1Qh6nYiuADWwKB4C2WSw==" crossorigin="anonymous"></script>
<script type="text/javascript" src="{{ url_for('static', path='/popover.js') }}"></script>
<script defer type="text/javascript" src="{{ url_for('static', path='/cursor.js') }}"></script>

<!-- 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>
Expand Down
27 changes: 27 additions & 0 deletions app/templates/cursor_settings.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{% extends "base.html" %}
{% block content %}
<div class="container mt-4">
<h1>Cursor Settings</h1>
<form action="{{ url_for('get_cursor_choices') }}" method="post" id="cursor-form">
<label for="primary-cusor">Select primary cursor :</label> <br>
<select name="primary_cursor" id="primary-cursor" class="form-control">
{% for cursor in cursors %}
<option value="{{ cursor }}.cur">{{cursor}}</option>
{% endfor %}
</select>
<br>
<label for="secondary-cusor">Select secondary cursor :</label> <br>
<select name="secondary_cursor" id="secondary-cursor" class="form-control">
{% for cursor in cursors %}
<option value="{{ cursor }}.cur">{{cursor}}</option>
{% endfor %}
</select>
<br>
<button id="activate-cursor" type="submit" form="cursor-form" class="btn btn-primary">
Activate
</button>
<br>
</form>
</div>

{% endblock %}
Loading