From df20e4804c99cbde77e0e711d386b45504c11bce Mon Sep 17 00:00:00 2001
From: Drew B <103056414+arbowl@users.noreply.github.com>
Date: Sat, 4 May 2024 22:51:39 -0400
Subject: [PATCH 1/6] Update for Python 3.12, fix minor visuals
---
ArabicLabel.py | 17 ++++
app.py | 222 +++++++++++++++++++++++++++++++++++++++++++++++++
login.html | 49 +++++++++++
main.py | 111 +++++++++++++++++++++++++
main_page.html | 26 ++++++
run_web.sh | 2 +-
tasks.html | 115 +++++++++++++++++++++++++
user.py | 54 ++++++++++++
8 files changed, 595 insertions(+), 1 deletion(-)
create mode 100644 ArabicLabel.py
create mode 100644 app.py
create mode 100644 login.html
create mode 100644 main.py
create mode 100644 main_page.html
create mode 100644 tasks.html
create mode 100644 user.py
diff --git a/ArabicLabel.py b/ArabicLabel.py
new file mode 100644
index 0000000..a07d3e2
--- /dev/null
+++ b/ArabicLabel.py
@@ -0,0 +1,17 @@
+from kivymd.label import MDLabel
+import arabic_reshaper
+from bidi.algorithm import get_display
+
+
+class ArabicLabel(MDLabel):
+ # def __init__( self, **kwargs ):
+ # super( ArabicLabel, self ).__init__( **kwargs );
+
+ # self.on_font_style( None, self.font_style )
+
+ # Overridden
+ def on_font_style(self, instance, style):
+ super(ArabicLabel, self).on_font_style(instance, style)
+
+ self.text = get_display(arabic_reshaper.reshape(self.text))
+ self.font_name = "fonts/KacstPenE"
diff --git a/app.py b/app.py
new file mode 100644
index 0000000..ff12757
--- /dev/null
+++ b/app.py
@@ -0,0 +1,222 @@
+from flask import Flask, render_template, request, make_response, redirect, Response
+from hashlib import md5
+from sqlite3 import connect, Connection, Cursor
+from time import strftime
+from typing import Optional
+from user import User
+
+app = Flask(__name__)
+
+
+def get_connection() -> tuple[Connection, Cursor]:
+ connection = connect("data.db")
+ cursor = connection.cursor()
+ return connection, cursor
+
+
+def is_installed() -> bool:
+ _, cursor = get_connection()
+ cursor.execute("SELECT COUNT( 1 ) FROM USERS")
+ has_user = cursor.fetchone()[0]
+ return has_user > 0
+
+
+def add_request(request_type) -> None:
+ connection, cursor = get_connection()
+ creation_date = strftime("%d-%m-%Y %H:%M:%S")
+ cursor.execute(
+ "INSERT INTO UPDATE_REQUESTS( UPDATE_TYPE, DONE, CREATION_DATE ) VALUES"
+ " ( ?, 'N', ? )",
+ (request_type, creation_date),
+ )
+ connection.commit()
+
+
+@app.before_request
+def before_request() -> Optional[str]:
+ if request.path == "/install":
+ return None
+ if not is_installed():
+ return render_template("install_prettypi.html")
+ if request.path == "/login":
+ return None
+ if request.cookies.get("prettypi_username") is not None:
+ User.set_username(request.cookies.get("prettypi_username"))
+ User.set_hashed_password(request.cookies.get("prettypi_password"))
+ return None if User.has_permission() else render_template("login.html")
+ return render_template("login.html")
+
+
+@app.route("/")
+def main() -> str:
+ return render_template("main_page.html", name=User.get_name())
+
+
+@app.route("/install", methods=["POST"])
+def install() -> str:
+ connection, cursor = get_connection()
+ if is_installed():
+ return render_template(
+ "installer_message.html",
+ message="PrettyPi Already Installed",
+ type="danger",
+ )
+ if (
+ not request.form["username"]
+ or not request.form["password"]
+ or not request.form["name"]
+ ):
+ return render_template(
+ "installer_message.html", message="Please fill all fields", type="danger"
+ )
+ hash_function = md5(request.form["password"].encode("utf-8"))
+ hashed_password = hash_function.hexdigest()
+ cursor.execute(
+ "INSERT INTO USERS( USER_ID, USERNAME, PASSWORD, NAME ) VALUES ( NULL, ?, ?, ? )",
+ [request.form["username"], hashed_password, request.form["name"]],
+ )
+ connection.commit()
+ return render_template(
+ "installer_message.html",
+ message="Congratulations! PrettyPi has been initialized. "\
+ "Go back to homepage to start using it",
+ type="success",
+ )
+
+
+@app.route("/login", methods=["POST"])
+def login() -> str:
+ User.set_username(request.form["username"])
+ User.set_password(request.form["password"].encode("utf-8"))
+ if User.has_permission():
+ response = make_response(redirect("/"))
+ response.set_cookie("prettypi_username", User.get_username())
+ response.set_cookie("prettypi_password", User.get_hashed_password())
+ return response
+ return "Invalid"
+
+
+@app.route("/logout")
+def logout() -> Response:
+ response = make_response(redirect("/"))
+ response.set_cookie("prettypi_username", "")
+ response.set_cookie("prettypi_password", "")
+ return response
+
+
+@app.route("/tasks")
+def tasks_main():
+ _, cursor = get_connection()
+ cursor.execute("SELECT * FROM TODO WHERE DONE = 'N' ORDER BY CREATION_DATE DESC")
+ tasks = cursor.fetchall()
+ cursor.execute("SELECT * FROM TODO WHERE DONE = 'Y' ORDER BY CREATION_DATE DESC")
+ done_tasks = cursor.fetchall()
+ cursor.execute("SELECT * FROM TODO WHERE WORKING_ON = 'Y'")
+ working_task = cursor.fetchone()
+ return render_template(
+ "tasks.html",
+ tasks=tasks,
+ done_tasks=done_tasks,
+ name=User.get_name(),
+ working_task=working_task,
+ )
+
+
+@app.route("/add_task", methods=["POST"])
+def new_task():
+ connection, cursor = get_connection()
+ if not request.form["task_details"]:
+ return render_template(
+ "message.html", message="The details should not be empty", type="warning"
+ )
+ creation_date = strftime("%d-%m-%Y %H:%M:%S")
+ cursor.execute(
+ "INSERT INTO TODO( TASK_ID, TASK, CREATION_DATE ) VALUES ( NULL, ?, ? )",
+ [request.form["task_details"], creation_date],
+ )
+ connection.commit()
+ add_request("UPDATE_TODO_LIST")
+ return redirect("tasks")
+
+
+@app.route("/delete_task", methods=["GET"])
+def delete_task() -> Response | str:
+ connection, cursor = get_connection()
+ task_id = request.args.get("task_id", None)
+ if task_id is None:
+ return "Invalid"
+ cursor.execute("DELETE FROM TODO WHERE TASK_ID = ?", [task_id])
+ cursor.execute("DELETE FROM TASKS_LOG WHERE TASK_ID = ?", [task_id])
+ connection.commit()
+ add_request("UPDATE_TODO_LIST")
+ return redirect("tasks")
+
+
+@app.route("/task_done", methods=["GET"])
+def mark_task_as_done() -> Response | str:
+ connection, cursor = get_connection()
+ task_id = request.args.get("task_id", None)
+ if task_id is None:
+ return "Invalid"
+ current_date = strftime("%d-%m-%Y %H:%M:%S")
+ cursor.execute(
+ "UPDATE TODO SET WORKING_ON = 'N', DONE = 'Y', DONE_AT = ? WHERE TASK_ID = ?",
+ [current_date, task_id],
+ )
+ connection.commit()
+ add_request("UPDATE_TODO_LIST")
+ return redirect("tasks")
+
+
+@app.route("/start_task", methods=["GET"])
+def start_working_on_task() -> Response | str:
+ connection, cursor = get_connection()
+ task_id = request.args.get("task_id", None)
+ if task_id is None:
+ return "Invalid"
+ cursor.execute(
+ "SELECT COUNT( 1 ) FROM TODO WHERE WORKING_ON = 'Y' AND TASK_ID <> ?",
+ [task_id]
+ )
+ currently_working_on = cursor.fetchone()[0]
+ if currently_working_on > 0:
+ return render_template(
+ "message.html",
+ message="You're already working on another task!",
+ type="warning",
+ )
+ creation_date = strftime("%d-%m-%Y %H:%M:%S")
+ cursor.execute("UPDATE TODO SET WORKING_ON = 'Y' WHERE TASK_ID = ?",[task_id])
+ cursor.execute(
+ "INSERT INTO TASKS_LOG( TASK_ID, START_AT ) VALUES ( ?, ? )",
+ [task_id, creation_date],
+ )
+ connection.commit()
+ add_request("UPDATE_TODO_LIST")
+ return redirect("tasks")
+
+
+@app.route("/stop_task", methods=["GET"])
+def stop_working_on_task() -> Response | str:
+ connection, cursor = get_connection()
+ task_id = request.args.get("task_id", None)
+ if task_id is None:
+ return "Invalid"
+ cursor.execute(
+ "SELECT MAX( log_id ) FROM tasks_log WHERE TASK_ID = ? AND ENDED_AT IS NULL",
+ [task_id],
+ )
+ current_task_log_id = cursor.fetchone()[0]
+ current_date = strftime("%d-%m-%Y %H:%M:%S")
+ cursor.execute("UPDATE TODO SET WORKING_ON = 'N' WHERE TASK_ID = ?", [task_id])
+ cursor.execute(
+ "UPDATE TASKS_LOG SET ENDED_AT = ? WHERE LOG_ID = ?",
+ [current_date, current_task_log_id],
+ )
+ connection.commit()
+ add_request("UPDATE_TODO_LIST")
+ return redirect("tasks")
+
+
+if __name__ == "__main__":
+ app.debug = True
diff --git a/login.html b/login.html
new file mode 100644
index 0000000..28e19fe
--- /dev/null
+++ b/login.html
@@ -0,0 +1,49 @@
+
+
+
+
+
+ PrettyPi | Log in
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/main.py b/main.py
new file mode 100644
index 0000000..35ba26c
--- /dev/null
+++ b/main.py
@@ -0,0 +1,111 @@
+#!/usr/bin/python
+# -*- coding: utf8 -*-
+
+# [MQH] 16 June 2017. It has been a while since last code I have written in Python! :-)
+from kivy.app import App
+from kivy.lang import Builder
+from kivymd.theming import ThemeManager
+from kivy.properties import ObjectProperty
+from kivymd.label import MDLabel
+from kivy.animation import Animation
+from ArabicLabel import ArabicLabel
+import arabic_reshaper
+import sqlite3
+import threading
+from kivy.clock import Clock
+
+kv_config = """
+#:import Toolbar kivymd.toolbar.Toolbar
+#:import NavigationLayout kivymd.navigationdrawer.NavigationLayout
+#:import MDNavigationDrawer kivymd.navigationdrawer.MDNavigationDrawer
+
+NavigationLayout:
+ id: navLayout
+
+ MDNavigationDrawer:
+ id: navDrawer
+ NavigationDrawerToolbar:
+ title: "Navigation Drawer"
+ NavigationDrawerIconButton:
+ id: quit_button
+ icon: 'checkbox-blank-circle'
+ text: "Quit"
+
+ BoxLayout:
+ id: topBox
+ orientation: 'vertical'
+ Toolbar:
+ id: toolbar
+ title: 'My Pretty Pi!'
+ md_bg_color: app.theme_cls.primary_color
+ background_palette: 'Primary'
+ background_hue: '500'
+ left_action_items: [['menu', lambda x: app.root.toggle_nav_drawer()]]
+ right_action_items: [['dots-vertical', lambda x: app.root.toggle_nav_drawer()]]
+
+ Toolbar:
+ id: titlebar
+ title: 'Current TODO'
+ md_bg_color: app.theme_cls.primary_color
+ background_palette: 'Primary'
+ background_hue: '900'
+
+ ScreenManager:
+ id: screenManager
+
+ Screen:
+ name: 'mainScreen'
+
+ BoxLayout:
+ id: main_box
+ orientation: 'vertical'
+ """
+
+
+class PrettyPiApp(App):
+ theme_cls = ThemeManager()
+ main_box = None
+ connection = None
+ cursor = None
+ kv_main = kv_config
+
+ def build(self):
+ main_widget = Builder.load_string(self.kv_main)
+ self.connection = sqlite3.connect("data.db")
+ self.cursor = self.connection.cursor()
+ self.main_box = main_widget.ids.mainBox
+ self.quit_button = main_widget.ids.quitBtn
+ self.quit_button.bind(on_press=lambda e: exit())
+ self.refresh_list()
+ Clock.schedule_interval(self.check_updates, 0.5)
+ return main_widget
+
+ def refresh_list(self) -> None:
+ self.main_box.clear_widgets()
+ self.cursor.execute("SELECT * FROM TODO WHERE DONE = 'N'")
+ tasks = self.cursor.fetchall()
+ for task in tasks:
+ task_text = task[1]
+ if task[5] == "Y":
+ task_text += " (Working On)"
+ self.main_box.add_widget(
+ ArabicLabel(text=task_text, halign="center", font_style="Display1")
+ )
+
+ def check_updates(self) -> None:
+ self.cursor.execute(
+ "SELECT COUNT( 1 ) FROM UPDATE_REQUESTS WHERE UPDATE_TYPE = "\
+ "'UPDATE_TODO_LIST' AND DONE = 'N'"
+ )
+ result = self.cursor.fetchone()
+ if result[0] > 0:
+ self.refresh_list()
+ self.cursor.execute(
+ "UPDATE UPDATE_REQUESTS SET DONE = 'Y' WHERE UPDATE_TYPE = "\
+ "'UPDATE_TODO_LIST'"
+ )
+ self.connection.commit()
+
+
+if __name__ == "__main__":
+ PrettyPiApp().run()
diff --git a/main_page.html b/main_page.html
new file mode 100644
index 0000000..1df378a
--- /dev/null
+++ b/main_page.html
@@ -0,0 +1,26 @@
+{% extends "base.html" %}
+
+{% block content %}
+
+
+
+
+
+
+
+
+
+
+
+{% endblock %}
diff --git a/run_web.sh b/run_web.sh
index 2c3418c..dc7ebb5 100755
--- a/run_web.sh
+++ b/run_web.sh
@@ -1,3 +1,3 @@
export FLASK_APP=control/app.py
export FLASK_DEBUG=1
-flask run --host=0.0.0.0
+flask run --host=0.0.0.0 --port=5000
diff --git a/tasks.html b/tasks.html
new file mode 100644
index 0000000..5b2e7d6
--- /dev/null
+++ b/tasks.html
@@ -0,0 +1,115 @@
+{% extends "base.html" %}
+
+{% block content %}
+
+
+
+
+
+
+
+
+ {% if working_task != None %}
+
+
+
+
+ Working On
+ {{ working_task.1 }}
+
+
+ {% endif %}
+
+
+
+
+
+
+
+
+
+
+ | # |
+ Task |
+ Start |
+ Done |
+ Delete |
+ Created On |
+
+ {% for task in tasks %}
+
+ | {{ loop.index }}. |
+ {{ task.1 }} |
+ {% if task.5 == 'N' %}
+ Start |
+ {% else %}
+ Stop |
+ {% endif %}
+ Done |
+ Delete |
+ {{ task.2 }} |
+
+ {% endfor %}
+
+
+
+
+
+
+
+
+
+ | # |
+ Task |
+ Created On |
+ Done At |
+ Delete |
+
+ {% for doneTask in doneTasks %}
+
+ | {{ loop.index }}. |
+ {{ doneTask.1 }} |
+ {{ doneTask.2 }} |
+ {{ doneTask.4 }} |
+ Delete |
+
+ {% endfor %}
+
+
+
+
+
+
+
+
+
+
+
+{% endblock %}
diff --git a/user.py b/user.py
new file mode 100644
index 0000000..b02b178
--- /dev/null
+++ b/user.py
@@ -0,0 +1,54 @@
+from hashlib import md5
+from sqlite3 import Connection, Cursor, connect
+from typing import Optional
+
+
+class User:
+ __username = Optional[str]
+ __password = Optional[str]
+ __name = Optional[str]
+ __cursor = Optional[Cursor]
+
+ @classmethod
+ def get_connection(cls) -> tuple[Connection, Cursor]:
+ connection = connect("data.db")
+ cursor = connection.cursor()
+ return connection, cursor
+
+ @classmethod
+ def has_permission(cls) -> bool:
+ _, cursor = cls.get_connection()
+ cursor.execute(
+ "SELECT * FROM USERS WHERE USERNAME = ? AND PASSWORD = ?",
+ (cls.__username, cls.__password),
+ )
+ result = cursor.fetchall()
+ if len(result) > 0:
+ cls.__name = result[0][3]
+ return True
+ return False
+
+ @classmethod
+ def set_username(cls, username: str) -> None:
+ cls.__username = username
+
+ @classmethod
+ def set_password(cls, password: str) -> None:
+ hashFunction = md5(password)
+ cls.__password = hashFunction.hexdigest()
+
+ @classmethod
+ def set_hashed_password(cls, hashedPassword) -> None:
+ cls.__password = hashedPassword
+
+ @classmethod
+ def get_username(cls) -> str:
+ return cls.__username
+
+ @classmethod
+ def get_hashed_password(cls) -> str:
+ return cls.__password
+
+ @classmethod
+ def get_name(cls) -> str:
+ return cls.__name
From 30eb53b254ec8030e88f3c432f8d449f65854184 Mon Sep 17 00:00:00 2001
From: Drew B <103056414+arbowl@users.noreply.github.com>
Date: Sat, 4 May 2024 22:54:04 -0400
Subject: [PATCH 2/6] Fix file structure from desktop app push
---
ArabicLabel.py | 17 --
app.py | 222 ---------------
control/app.py | 464 ++++++++++++++-----------------
control/templates/login.html | 99 +++----
control/templates/main_page.html | 1 +
control/templates/tasks.html | 59 ++--
control/user.py | 100 +++----
login.html | 49 ----
main.py | 111 --------
main_page.html | 26 --
server/ArabicLabel.py | 19 +-
server/main.py | 116 ++++----
tasks.html | 115 --------
user.py | 54 ----
14 files changed, 397 insertions(+), 1055 deletions(-)
delete mode 100644 ArabicLabel.py
delete mode 100644 app.py
delete mode 100644 login.html
delete mode 100644 main.py
delete mode 100644 main_page.html
delete mode 100644 tasks.html
delete mode 100644 user.py
diff --git a/ArabicLabel.py b/ArabicLabel.py
deleted file mode 100644
index a07d3e2..0000000
--- a/ArabicLabel.py
+++ /dev/null
@@ -1,17 +0,0 @@
-from kivymd.label import MDLabel
-import arabic_reshaper
-from bidi.algorithm import get_display
-
-
-class ArabicLabel(MDLabel):
- # def __init__( self, **kwargs ):
- # super( ArabicLabel, self ).__init__( **kwargs );
-
- # self.on_font_style( None, self.font_style )
-
- # Overridden
- def on_font_style(self, instance, style):
- super(ArabicLabel, self).on_font_style(instance, style)
-
- self.text = get_display(arabic_reshaper.reshape(self.text))
- self.font_name = "fonts/KacstPenE"
diff --git a/app.py b/app.py
deleted file mode 100644
index ff12757..0000000
--- a/app.py
+++ /dev/null
@@ -1,222 +0,0 @@
-from flask import Flask, render_template, request, make_response, redirect, Response
-from hashlib import md5
-from sqlite3 import connect, Connection, Cursor
-from time import strftime
-from typing import Optional
-from user import User
-
-app = Flask(__name__)
-
-
-def get_connection() -> tuple[Connection, Cursor]:
- connection = connect("data.db")
- cursor = connection.cursor()
- return connection, cursor
-
-
-def is_installed() -> bool:
- _, cursor = get_connection()
- cursor.execute("SELECT COUNT( 1 ) FROM USERS")
- has_user = cursor.fetchone()[0]
- return has_user > 0
-
-
-def add_request(request_type) -> None:
- connection, cursor = get_connection()
- creation_date = strftime("%d-%m-%Y %H:%M:%S")
- cursor.execute(
- "INSERT INTO UPDATE_REQUESTS( UPDATE_TYPE, DONE, CREATION_DATE ) VALUES"
- " ( ?, 'N', ? )",
- (request_type, creation_date),
- )
- connection.commit()
-
-
-@app.before_request
-def before_request() -> Optional[str]:
- if request.path == "/install":
- return None
- if not is_installed():
- return render_template("install_prettypi.html")
- if request.path == "/login":
- return None
- if request.cookies.get("prettypi_username") is not None:
- User.set_username(request.cookies.get("prettypi_username"))
- User.set_hashed_password(request.cookies.get("prettypi_password"))
- return None if User.has_permission() else render_template("login.html")
- return render_template("login.html")
-
-
-@app.route("/")
-def main() -> str:
- return render_template("main_page.html", name=User.get_name())
-
-
-@app.route("/install", methods=["POST"])
-def install() -> str:
- connection, cursor = get_connection()
- if is_installed():
- return render_template(
- "installer_message.html",
- message="PrettyPi Already Installed",
- type="danger",
- )
- if (
- not request.form["username"]
- or not request.form["password"]
- or not request.form["name"]
- ):
- return render_template(
- "installer_message.html", message="Please fill all fields", type="danger"
- )
- hash_function = md5(request.form["password"].encode("utf-8"))
- hashed_password = hash_function.hexdigest()
- cursor.execute(
- "INSERT INTO USERS( USER_ID, USERNAME, PASSWORD, NAME ) VALUES ( NULL, ?, ?, ? )",
- [request.form["username"], hashed_password, request.form["name"]],
- )
- connection.commit()
- return render_template(
- "installer_message.html",
- message="Congratulations! PrettyPi has been initialized. "\
- "Go back to homepage to start using it",
- type="success",
- )
-
-
-@app.route("/login", methods=["POST"])
-def login() -> str:
- User.set_username(request.form["username"])
- User.set_password(request.form["password"].encode("utf-8"))
- if User.has_permission():
- response = make_response(redirect("/"))
- response.set_cookie("prettypi_username", User.get_username())
- response.set_cookie("prettypi_password", User.get_hashed_password())
- return response
- return "Invalid"
-
-
-@app.route("/logout")
-def logout() -> Response:
- response = make_response(redirect("/"))
- response.set_cookie("prettypi_username", "")
- response.set_cookie("prettypi_password", "")
- return response
-
-
-@app.route("/tasks")
-def tasks_main():
- _, cursor = get_connection()
- cursor.execute("SELECT * FROM TODO WHERE DONE = 'N' ORDER BY CREATION_DATE DESC")
- tasks = cursor.fetchall()
- cursor.execute("SELECT * FROM TODO WHERE DONE = 'Y' ORDER BY CREATION_DATE DESC")
- done_tasks = cursor.fetchall()
- cursor.execute("SELECT * FROM TODO WHERE WORKING_ON = 'Y'")
- working_task = cursor.fetchone()
- return render_template(
- "tasks.html",
- tasks=tasks,
- done_tasks=done_tasks,
- name=User.get_name(),
- working_task=working_task,
- )
-
-
-@app.route("/add_task", methods=["POST"])
-def new_task():
- connection, cursor = get_connection()
- if not request.form["task_details"]:
- return render_template(
- "message.html", message="The details should not be empty", type="warning"
- )
- creation_date = strftime("%d-%m-%Y %H:%M:%S")
- cursor.execute(
- "INSERT INTO TODO( TASK_ID, TASK, CREATION_DATE ) VALUES ( NULL, ?, ? )",
- [request.form["task_details"], creation_date],
- )
- connection.commit()
- add_request("UPDATE_TODO_LIST")
- return redirect("tasks")
-
-
-@app.route("/delete_task", methods=["GET"])
-def delete_task() -> Response | str:
- connection, cursor = get_connection()
- task_id = request.args.get("task_id", None)
- if task_id is None:
- return "Invalid"
- cursor.execute("DELETE FROM TODO WHERE TASK_ID = ?", [task_id])
- cursor.execute("DELETE FROM TASKS_LOG WHERE TASK_ID = ?", [task_id])
- connection.commit()
- add_request("UPDATE_TODO_LIST")
- return redirect("tasks")
-
-
-@app.route("/task_done", methods=["GET"])
-def mark_task_as_done() -> Response | str:
- connection, cursor = get_connection()
- task_id = request.args.get("task_id", None)
- if task_id is None:
- return "Invalid"
- current_date = strftime("%d-%m-%Y %H:%M:%S")
- cursor.execute(
- "UPDATE TODO SET WORKING_ON = 'N', DONE = 'Y', DONE_AT = ? WHERE TASK_ID = ?",
- [current_date, task_id],
- )
- connection.commit()
- add_request("UPDATE_TODO_LIST")
- return redirect("tasks")
-
-
-@app.route("/start_task", methods=["GET"])
-def start_working_on_task() -> Response | str:
- connection, cursor = get_connection()
- task_id = request.args.get("task_id", None)
- if task_id is None:
- return "Invalid"
- cursor.execute(
- "SELECT COUNT( 1 ) FROM TODO WHERE WORKING_ON = 'Y' AND TASK_ID <> ?",
- [task_id]
- )
- currently_working_on = cursor.fetchone()[0]
- if currently_working_on > 0:
- return render_template(
- "message.html",
- message="You're already working on another task!",
- type="warning",
- )
- creation_date = strftime("%d-%m-%Y %H:%M:%S")
- cursor.execute("UPDATE TODO SET WORKING_ON = 'Y' WHERE TASK_ID = ?",[task_id])
- cursor.execute(
- "INSERT INTO TASKS_LOG( TASK_ID, START_AT ) VALUES ( ?, ? )",
- [task_id, creation_date],
- )
- connection.commit()
- add_request("UPDATE_TODO_LIST")
- return redirect("tasks")
-
-
-@app.route("/stop_task", methods=["GET"])
-def stop_working_on_task() -> Response | str:
- connection, cursor = get_connection()
- task_id = request.args.get("task_id", None)
- if task_id is None:
- return "Invalid"
- cursor.execute(
- "SELECT MAX( log_id ) FROM tasks_log WHERE TASK_ID = ? AND ENDED_AT IS NULL",
- [task_id],
- )
- current_task_log_id = cursor.fetchone()[0]
- current_date = strftime("%d-%m-%Y %H:%M:%S")
- cursor.execute("UPDATE TODO SET WORKING_ON = 'N' WHERE TASK_ID = ?", [task_id])
- cursor.execute(
- "UPDATE TASKS_LOG SET ENDED_AT = ? WHERE LOG_ID = ?",
- [current_date, current_task_log_id],
- )
- connection.commit()
- add_request("UPDATE_TODO_LIST")
- return redirect("tasks")
-
-
-if __name__ == "__main__":
- app.debug = True
diff --git a/control/app.py b/control/app.py
index 5a05d64..ff12757 100644
--- a/control/app.py
+++ b/control/app.py
@@ -1,262 +1,222 @@
-from flask import Flask, render_template, request, make_response, redirect;
-from user import User;
-import sqlite3;
-from time import strftime;
-import hashlib;
+from flask import Flask, render_template, request, make_response, redirect, Response
+from hashlib import md5
+from sqlite3 import connect, Connection, Cursor
+from time import strftime
+from typing import Optional
+from user import User
-app = Flask( __name__ );
-app.debug = True;
+app = Flask(__name__)
-user = User();
-def getConnection():
- connection = sqlite3.connect( 'data.db' );
- cursor = connection.cursor();
-
- return ( connection, cursor )
+def get_connection() -> tuple[Connection, Cursor]:
+ connection = connect("data.db")
+ cursor = connection.cursor()
+ return connection, cursor
-# ... #
-def isInstalled():
- ( connection, cursor ) = getConnection();
-
- cursor.execute( "SELECT COUNT( 1 ) FROM USERS" );
- hasUser = cursor.fetchone()[ 0 ];
+def is_installed() -> bool:
+ _, cursor = get_connection()
+ cursor.execute("SELECT COUNT( 1 ) FROM USERS")
+ has_user = cursor.fetchone()[0]
+ return has_user > 0
- return ( hasUser > 0 );
-def addRequest( reqType ):
- ( connection, cursor ) = getConnection();
-
- creationDate = strftime( '%d-%m-%Y %H:%M:%S' );
+def add_request(request_type) -> None:
+ connection, cursor = get_connection()
+ creation_date = strftime("%d-%m-%Y %H:%M:%S")
+ cursor.execute(
+ "INSERT INTO UPDATE_REQUESTS( UPDATE_TYPE, DONE, CREATION_DATE ) VALUES"
+ " ( ?, 'N', ? )",
+ (request_type, creation_date),
+ )
+ connection.commit()
- cursor.execute( "INSERT INTO UPDATE_REQUESTS( UPDATE_TYPE, DONE, CREATION_DATE ) VALUES ( ?, 'N', ? )", [ reqType, creationDate ] );
-
- connection.commit();
@app.before_request
-def beforeRequest():
- if request.path == '/install':
- return None;
-
- if not isInstalled():
- return render_template( 'install_prettypi.html' );
-
- if request.path == '/login':
- return None;
-
- if request.cookies.get( 'prettypi_username' ) != None:
- user.setUsername( request.cookies.get( 'prettypi_username' ) );
- user.setHashedPassword( request.cookies.get( 'prettypi_password' ) );
-
- if not user.hasPermission():
- return render_template( 'login.html' );
- else:
- return None;
-
- return render_template( 'login.html' );
-
-# ... #
-
-@app.route( '/' )
-def main():
- return render_template( 'main_page.html', name = user.getName() );
-
-# ... #
-
-@app.route( '/install', methods = [ 'POST' ] )
-def install():
- ( connection, cursor ) = getConnection();
-
- if isInstalled():
- return render_template( 'installer_message.html', message = 'PrettyPi Already Installed', type = 'danger' );
-
- if not request.form[ 'username' ] or not request.form[ 'password' ] or not request.form[ 'name' ]:
- return render_template( 'installer_message.html', message = 'Please fill all fields', type = 'danger' );
-
- hashFunction = hashlib.md5( request.form[ 'password' ].encode( 'utf-8' ) );
-
- hashedPassword = hashFunction.hexdigest();
-
- cursor.execute( "INSERT INTO USERS( USER_ID, USERNAME, PASSWORD, NAME ) VALUES ( NULL, ?, ?, ? )", [ request.form[ 'username' ], hashedPassword, request.form[ 'name' ] ] );
-
- connection.commit();
-
- return render_template( 'installer_message.html', message = 'Congratulations! PrettyPi has been initialized. Go back to homepage to start using it', type = 'success' );
-
-# ... #
-
-@app.route( '/login', methods = [ 'POST' ] )
-def login():
- user.setUsername( request.form[ 'username' ] );
- user.setPassword( request.form[ 'password' ].encode( 'utf-8' ) );
-
- if user.hasPermission():
- response = make_response( redirect( '/' ) );
- response.set_cookie( 'prettypi_username', user.getUsername() );
- response.set_cookie( 'prettypi_password', user.getHashedPassword() );
-
- return response;
- else:
- return 'Invalid';
-
-# ... #
-
-@app.route( '/logout' )
-def logout():
- response = make_response( redirect( '/' ) );
- response.set_cookie( 'prettypi_username', '' );
- response.set_cookie( 'prettypi_password', '' );
-
- return response;
-
-# ... #
-
-@app.route( '/tasks' )
-def tasksMain():
- ( connection, cursor ) = getConnection();
-
- cursor.execute( "SELECT * FROM TODO WHERE DONE = 'N' ORDER BY CREATION_DATE DESC" );
-
- tasks = cursor.fetchall();
-
- # ... #
-
- cursor.execute( "SELECT * FROM TODO WHERE DONE = 'Y' ORDER BY CREATION_DATE DESC" );
-
- doneTasks = cursor.fetchall();
-
- # ... #
-
- cursor.execute( "SELECT * FROM TODO WHERE WORKING_ON = 'Y'" );
-
- workingTask = cursor.fetchone();
-
- print( workingTask );
-
- # ... #
-
- return render_template( 'tasks.html', tasks = tasks, doneTasks = doneTasks, name = user.getName(), workingTask = workingTask );
-
-# ... #
-
-@app.route( '/add_task', methods = [ 'POST' ] )
-def newTask():
- ( connection, cursor ) = getConnection();
-
- if not request.form[ 'task_details' ]:
- return render_template( 'message.html', message = 'The details should not be empty', type = 'warning' );
-
- creationDate = strftime( '%d-%m-%Y %H:%M:%S' );
-
- cursor.execute( "INSERT INTO TODO( TASK_ID, TASK, CREATION_DATE ) VALUES ( NULL, ?, ? )", [ request.form[ 'task_details' ], creationDate ] );
-
- connection.commit();
-
- addRequest( "UPDATE_TODO_LIST" );
-
- return redirect( 'tasks' );
-
-# ... #
-
-@app.route( '/delete_task', methods = [ 'GET' ] )
-def deleteTask():
- ( connection, cursor ) = getConnection();
-
- taskId = request.args.get( 'task_id', None );
-
- if taskId is None:
- return 'Invalid';
-
- cursor.execute( "DELETE FROM TODO WHERE TASK_ID = ?", [ taskId ] );
- cursor.execute( "DELETE FROM TASKS_LOG WHERE TASK_ID = ?", [ taskId ] );
-
- connection.commit();
-
- addRequest( "UPDATE_TODO_LIST" );
-
- return redirect( 'tasks' );
-
-@app.route( '/task_done', methods = [ 'GET' ] )
-def markTaskAsDone():
- ( connection, cursor ) = getConnection();
-
- taskId = request.args.get( 'task_id', None );
-
- if taskId is None:
- return 'Invalid';
-
- currentDate = strftime( '%d-%m-%Y %H:%M:%S' );
-
- cursor.execute( "UPDATE TODO SET WORKING_ON = 'N', DONE = 'Y', DONE_AT = ? WHERE TASK_ID = ?", [ currentDate, taskId ] );
-
- connection.commit();
-
- addRequest( "UPDATE_TODO_LIST" );
-
- return redirect( 'tasks' );
-
-@app.route( '/start_task', methods = [ 'GET' ] )
-def startWorkingOnTask():
- ( connection, cursor ) = getConnection();
-
- taskId = request.args.get( 'task_id', None );
-
- if taskId is None:
- return 'Invalid';
-
- # ... #
-
- cursor.execute( "SELECT COUNT( 1 ) FROM TODO WHERE WORKING_ON = 'Y' AND TASK_ID <> ?", [ taskId ] );
-
- currentlyWorkingOn = cursor.fetchone()[ 0 ];
-
- if currentlyWorkingOn > 0:
- return render_template( 'message.html', message = "You're already working on another task!", type = 'warning' );
-
- # ... #
-
- creationDate = strftime( '%d-%m-%Y %H:%M:%S' );
-
- cursor.execute( "UPDATE TODO SET WORKING_ON = 'Y' WHERE TASK_ID = ?", [ taskId ] );
- cursor.execute( "INSERT INTO TASKS_LOG( TASK_ID, START_AT ) VALUES ( ?, ? )", [ taskId, creationDate ] );
-
- connection.commit();
-
- # ... #
-
- addRequest( "UPDATE_TODO_LIST" );
-
- return redirect( 'tasks' );
-
-# ... #
-
-@app.route( '/stop_task', methods = [ 'GET' ] )
-def stopWorkingOnTask():
- ( connection, cursor ) = getConnection();
-
- taskId = request.args.get( 'task_id', None );
-
- if taskId is None:
- return 'Invalid';
-
- # ... #
-
- cursor.execute( "SELECT MAX( log_id ) FROM tasks_log WHERE TASK_ID = ? AND ENDED_AT IS NULL", [ taskId ] );
-
- currTaskLogId = cursor.fetchone()[ 0 ];
-
- # ... #
-
- currentDate = strftime( '%d-%m-%Y %H:%M:%S' );
-
- cursor.execute( "UPDATE TODO SET WORKING_ON = 'N' WHERE TASK_ID = ?", [ taskId ] );
- cursor.execute( "UPDATE TASKS_LOG SET ENDED_AT = ? WHERE LOG_ID = ?", [ currentDate, currTaskLogId ] );
-
- connection.commit();
-
- # ... #
-
- addRequest( "UPDATE_TODO_LIST" );
-
- return redirect( 'tasks' );
+def before_request() -> Optional[str]:
+ if request.path == "/install":
+ return None
+ if not is_installed():
+ return render_template("install_prettypi.html")
+ if request.path == "/login":
+ return None
+ if request.cookies.get("prettypi_username") is not None:
+ User.set_username(request.cookies.get("prettypi_username"))
+ User.set_hashed_password(request.cookies.get("prettypi_password"))
+ return None if User.has_permission() else render_template("login.html")
+ return render_template("login.html")
+
+
+@app.route("/")
+def main() -> str:
+ return render_template("main_page.html", name=User.get_name())
+
+
+@app.route("/install", methods=["POST"])
+def install() -> str:
+ connection, cursor = get_connection()
+ if is_installed():
+ return render_template(
+ "installer_message.html",
+ message="PrettyPi Already Installed",
+ type="danger",
+ )
+ if (
+ not request.form["username"]
+ or not request.form["password"]
+ or not request.form["name"]
+ ):
+ return render_template(
+ "installer_message.html", message="Please fill all fields", type="danger"
+ )
+ hash_function = md5(request.form["password"].encode("utf-8"))
+ hashed_password = hash_function.hexdigest()
+ cursor.execute(
+ "INSERT INTO USERS( USER_ID, USERNAME, PASSWORD, NAME ) VALUES ( NULL, ?, ?, ? )",
+ [request.form["username"], hashed_password, request.form["name"]],
+ )
+ connection.commit()
+ return render_template(
+ "installer_message.html",
+ message="Congratulations! PrettyPi has been initialized. "\
+ "Go back to homepage to start using it",
+ type="success",
+ )
+
+
+@app.route("/login", methods=["POST"])
+def login() -> str:
+ User.set_username(request.form["username"])
+ User.set_password(request.form["password"].encode("utf-8"))
+ if User.has_permission():
+ response = make_response(redirect("/"))
+ response.set_cookie("prettypi_username", User.get_username())
+ response.set_cookie("prettypi_password", User.get_hashed_password())
+ return response
+ return "Invalid"
+
+
+@app.route("/logout")
+def logout() -> Response:
+ response = make_response(redirect("/"))
+ response.set_cookie("prettypi_username", "")
+ response.set_cookie("prettypi_password", "")
+ return response
+
+
+@app.route("/tasks")
+def tasks_main():
+ _, cursor = get_connection()
+ cursor.execute("SELECT * FROM TODO WHERE DONE = 'N' ORDER BY CREATION_DATE DESC")
+ tasks = cursor.fetchall()
+ cursor.execute("SELECT * FROM TODO WHERE DONE = 'Y' ORDER BY CREATION_DATE DESC")
+ done_tasks = cursor.fetchall()
+ cursor.execute("SELECT * FROM TODO WHERE WORKING_ON = 'Y'")
+ working_task = cursor.fetchone()
+ return render_template(
+ "tasks.html",
+ tasks=tasks,
+ done_tasks=done_tasks,
+ name=User.get_name(),
+ working_task=working_task,
+ )
+
+
+@app.route("/add_task", methods=["POST"])
+def new_task():
+ connection, cursor = get_connection()
+ if not request.form["task_details"]:
+ return render_template(
+ "message.html", message="The details should not be empty", type="warning"
+ )
+ creation_date = strftime("%d-%m-%Y %H:%M:%S")
+ cursor.execute(
+ "INSERT INTO TODO( TASK_ID, TASK, CREATION_DATE ) VALUES ( NULL, ?, ? )",
+ [request.form["task_details"], creation_date],
+ )
+ connection.commit()
+ add_request("UPDATE_TODO_LIST")
+ return redirect("tasks")
+
+
+@app.route("/delete_task", methods=["GET"])
+def delete_task() -> Response | str:
+ connection, cursor = get_connection()
+ task_id = request.args.get("task_id", None)
+ if task_id is None:
+ return "Invalid"
+ cursor.execute("DELETE FROM TODO WHERE TASK_ID = ?", [task_id])
+ cursor.execute("DELETE FROM TASKS_LOG WHERE TASK_ID = ?", [task_id])
+ connection.commit()
+ add_request("UPDATE_TODO_LIST")
+ return redirect("tasks")
+
+
+@app.route("/task_done", methods=["GET"])
+def mark_task_as_done() -> Response | str:
+ connection, cursor = get_connection()
+ task_id = request.args.get("task_id", None)
+ if task_id is None:
+ return "Invalid"
+ current_date = strftime("%d-%m-%Y %H:%M:%S")
+ cursor.execute(
+ "UPDATE TODO SET WORKING_ON = 'N', DONE = 'Y', DONE_AT = ? WHERE TASK_ID = ?",
+ [current_date, task_id],
+ )
+ connection.commit()
+ add_request("UPDATE_TODO_LIST")
+ return redirect("tasks")
+
+
+@app.route("/start_task", methods=["GET"])
+def start_working_on_task() -> Response | str:
+ connection, cursor = get_connection()
+ task_id = request.args.get("task_id", None)
+ if task_id is None:
+ return "Invalid"
+ cursor.execute(
+ "SELECT COUNT( 1 ) FROM TODO WHERE WORKING_ON = 'Y' AND TASK_ID <> ?",
+ [task_id]
+ )
+ currently_working_on = cursor.fetchone()[0]
+ if currently_working_on > 0:
+ return render_template(
+ "message.html",
+ message="You're already working on another task!",
+ type="warning",
+ )
+ creation_date = strftime("%d-%m-%Y %H:%M:%S")
+ cursor.execute("UPDATE TODO SET WORKING_ON = 'Y' WHERE TASK_ID = ?",[task_id])
+ cursor.execute(
+ "INSERT INTO TASKS_LOG( TASK_ID, START_AT ) VALUES ( ?, ? )",
+ [task_id, creation_date],
+ )
+ connection.commit()
+ add_request("UPDATE_TODO_LIST")
+ return redirect("tasks")
+
+
+@app.route("/stop_task", methods=["GET"])
+def stop_working_on_task() -> Response | str:
+ connection, cursor = get_connection()
+ task_id = request.args.get("task_id", None)
+ if task_id is None:
+ return "Invalid"
+ cursor.execute(
+ "SELECT MAX( log_id ) FROM tasks_log WHERE TASK_ID = ? AND ENDED_AT IS NULL",
+ [task_id],
+ )
+ current_task_log_id = cursor.fetchone()[0]
+ current_date = strftime("%d-%m-%Y %H:%M:%S")
+ cursor.execute("UPDATE TODO SET WORKING_ON = 'N' WHERE TASK_ID = ?", [task_id])
+ cursor.execute(
+ "UPDATE TASKS_LOG SET ENDED_AT = ? WHERE LOG_ID = ?",
+ [current_date, current_task_log_id],
+ )
+ connection.commit()
+ add_request("UPDATE_TODO_LIST")
+ return redirect("tasks")
+
+
+if __name__ == "__main__":
+ app.debug = True
diff --git a/control/templates/login.html b/control/templates/login.html
index 91ebd8b..28e19fe 100644
--- a/control/templates/login.html
+++ b/control/templates/login.html
@@ -1,70 +1,49 @@
-
+
-
-
+
+
PrettyPi | Log in
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
-
-
-
-
-
+
| # |
Task |
+ Start |
+ Done |
+ Delete |
Created On |
- Done At |
- Delete |
- {% for doneTask in doneTasks %}
+ {% for task in tasks %}
| {{ loop.index }}. |
- {{ doneTask.1 }} |
- {{ doneTask.2 }} |
- {{ doneTask.4 }} |
- Delete |
+ {{ task.1 }} |
+ {% if task.5 == 'N' %}
+ Start |
+ {% else %}
+ Stop |
+ {% endif %}
+ Done |
+ Delete |
+ {{ task.2 }} |
{% endfor %}
-
-
-
+
-
+
-
+
| # |
Task |
- Start |
- Done |
- Delete |
Created On |
+ Done At |
+ Delete |
- {% for task in tasks %}
+ {% for doneTask in doneTasks %}
| {{ loop.index }}. |
- {{ task.1 }} |
- {% if task.5 == 'N' %}
- Start |
- {% else %}
- Stop |
- {% endif %}
- Done |
- Delete |
- {{ task.2 }} |
+ {{ doneTask.1 }} |
+ {{ doneTask.2 }} |
+ {{ doneTask.4 }} |
+ Delete |
{% endfor %}
+
diff --git a/control/user.py b/control/user.py
index bcdfb62..b02b178 100644
--- a/control/user.py
+++ b/control/user.py
@@ -1,50 +1,54 @@
-import sqlite3;
-import hashlib;
+from hashlib import md5
+from sqlite3 import Connection, Cursor, connect
+from typing import Optional
-class User:
- __username = None;
- __password = None;
- __name = None;
- __cursor = None;
-
- def getConnection( self ):
- connection = sqlite3.connect( 'data.db' );
- cursor = connection.cursor();
-
- return ( connection, cursor )
-
- def hasPermission( self ):
- ( connection, cursor ) = self.getConnection();
-
- print ( self.__username, self.__password );
-
- cursor.execute( 'SELECT * FROM USERS WHERE USERNAME = ? AND PASSWORD = ?', ( self.__username, self.__password ) );
-
- result = cursor.fetchall();
-
- if len( result ) > 0:
- self.__name = result[ 0 ][ 3 ];
-
- return True;
-
- return False;
-
- def setUsername( self, username ):
- self.__username = username;
- def setPassword( self, password ):
- hashFunction = hashlib.md5( password );
-
- self.__password = hashFunction.hexdigest();
-
- def setHashedPassword( self, hashedPassword ):
- self.__password = hashedPassword;
-
- def getUsername( self ):
- return self.__username;
-
- def getHashedPassword( self ):
- return self.__password;
-
- def getName( self ):
- return self.__name;
+class User:
+ __username = Optional[str]
+ __password = Optional[str]
+ __name = Optional[str]
+ __cursor = Optional[Cursor]
+
+ @classmethod
+ def get_connection(cls) -> tuple[Connection, Cursor]:
+ connection = connect("data.db")
+ cursor = connection.cursor()
+ return connection, cursor
+
+ @classmethod
+ def has_permission(cls) -> bool:
+ _, cursor = cls.get_connection()
+ cursor.execute(
+ "SELECT * FROM USERS WHERE USERNAME = ? AND PASSWORD = ?",
+ (cls.__username, cls.__password),
+ )
+ result = cursor.fetchall()
+ if len(result) > 0:
+ cls.__name = result[0][3]
+ return True
+ return False
+
+ @classmethod
+ def set_username(cls, username: str) -> None:
+ cls.__username = username
+
+ @classmethod
+ def set_password(cls, password: str) -> None:
+ hashFunction = md5(password)
+ cls.__password = hashFunction.hexdigest()
+
+ @classmethod
+ def set_hashed_password(cls, hashedPassword) -> None:
+ cls.__password = hashedPassword
+
+ @classmethod
+ def get_username(cls) -> str:
+ return cls.__username
+
+ @classmethod
+ def get_hashed_password(cls) -> str:
+ return cls.__password
+
+ @classmethod
+ def get_name(cls) -> str:
+ return cls.__name
diff --git a/login.html b/login.html
deleted file mode 100644
index 28e19fe..0000000
--- a/login.html
+++ /dev/null
@@ -1,49 +0,0 @@
-
-
-
-
-
-
PrettyPi | Log in
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/main.py b/main.py
deleted file mode 100644
index 35ba26c..0000000
--- a/main.py
+++ /dev/null
@@ -1,111 +0,0 @@
-#!/usr/bin/python
-# -*- coding: utf8 -*-
-
-# [MQH] 16 June 2017. It has been a while since last code I have written in Python! :-)
-from kivy.app import App
-from kivy.lang import Builder
-from kivymd.theming import ThemeManager
-from kivy.properties import ObjectProperty
-from kivymd.label import MDLabel
-from kivy.animation import Animation
-from ArabicLabel import ArabicLabel
-import arabic_reshaper
-import sqlite3
-import threading
-from kivy.clock import Clock
-
-kv_config = """
-#:import Toolbar kivymd.toolbar.Toolbar
-#:import NavigationLayout kivymd.navigationdrawer.NavigationLayout
-#:import MDNavigationDrawer kivymd.navigationdrawer.MDNavigationDrawer
-
-NavigationLayout:
- id: navLayout
-
- MDNavigationDrawer:
- id: navDrawer
- NavigationDrawerToolbar:
- title: "Navigation Drawer"
- NavigationDrawerIconButton:
- id: quit_button
- icon: 'checkbox-blank-circle'
- text: "Quit"
-
- BoxLayout:
- id: topBox
- orientation: 'vertical'
- Toolbar:
- id: toolbar
- title: 'My Pretty Pi!'
- md_bg_color: app.theme_cls.primary_color
- background_palette: 'Primary'
- background_hue: '500'
- left_action_items: [['menu', lambda x: app.root.toggle_nav_drawer()]]
- right_action_items: [['dots-vertical', lambda x: app.root.toggle_nav_drawer()]]
-
- Toolbar:
- id: titlebar
- title: 'Current TODO'
- md_bg_color: app.theme_cls.primary_color
- background_palette: 'Primary'
- background_hue: '900'
-
- ScreenManager:
- id: screenManager
-
- Screen:
- name: 'mainScreen'
-
- BoxLayout:
- id: main_box
- orientation: 'vertical'
- """
-
-
-class PrettyPiApp(App):
- theme_cls = ThemeManager()
- main_box = None
- connection = None
- cursor = None
- kv_main = kv_config
-
- def build(self):
- main_widget = Builder.load_string(self.kv_main)
- self.connection = sqlite3.connect("data.db")
- self.cursor = self.connection.cursor()
- self.main_box = main_widget.ids.mainBox
- self.quit_button = main_widget.ids.quitBtn
- self.quit_button.bind(on_press=lambda e: exit())
- self.refresh_list()
- Clock.schedule_interval(self.check_updates, 0.5)
- return main_widget
-
- def refresh_list(self) -> None:
- self.main_box.clear_widgets()
- self.cursor.execute("SELECT * FROM TODO WHERE DONE = 'N'")
- tasks = self.cursor.fetchall()
- for task in tasks:
- task_text = task[1]
- if task[5] == "Y":
- task_text += " (Working On)"
- self.main_box.add_widget(
- ArabicLabel(text=task_text, halign="center", font_style="Display1")
- )
-
- def check_updates(self) -> None:
- self.cursor.execute(
- "SELECT COUNT( 1 ) FROM UPDATE_REQUESTS WHERE UPDATE_TYPE = "\
- "'UPDATE_TODO_LIST' AND DONE = 'N'"
- )
- result = self.cursor.fetchone()
- if result[0] > 0:
- self.refresh_list()
- self.cursor.execute(
- "UPDATE UPDATE_REQUESTS SET DONE = 'Y' WHERE UPDATE_TYPE = "\
- "'UPDATE_TODO_LIST'"
- )
- self.connection.commit()
-
-
-if __name__ == "__main__":
- PrettyPiApp().run()
diff --git a/main_page.html b/main_page.html
deleted file mode 100644
index 1df378a..0000000
--- a/main_page.html
+++ /dev/null
@@ -1,26 +0,0 @@
-{% extends "base.html" %}
-
-{% block content %}
-
-
-
-
-
-
-
-
-
-
-
-{% endblock %}
diff --git a/server/ArabicLabel.py b/server/ArabicLabel.py
index 53b80b3..a07d3e2 100644
--- a/server/ArabicLabel.py
+++ b/server/ArabicLabel.py
@@ -2,15 +2,16 @@
import arabic_reshaper
from bidi.algorithm import get_display
-class ArabicLabel( MDLabel ):
- #def __init__( self, **kwargs ):
- # super( ArabicLabel, self ).__init__( **kwargs );
- #self.on_font_style( None, self.font_style )
+class ArabicLabel(MDLabel):
+ # def __init__( self, **kwargs ):
+ # super( ArabicLabel, self ).__init__( **kwargs );
- # Overridden
- def on_font_style( self, instance, style ):
- super( ArabicLabel, self ).on_font_style( instance, style );
+ # self.on_font_style( None, self.font_style )
- self.text = get_display( arabic_reshaper.reshape( self.text ) );
- self.font_name = 'fonts/KacstPenE';
+ # Overridden
+ def on_font_style(self, instance, style):
+ super(ArabicLabel, self).on_font_style(instance, style)
+
+ self.text = get_display(arabic_reshaper.reshape(self.text))
+ self.font_name = "fonts/KacstPenE"
diff --git a/server/main.py b/server/main.py
index 76375c1..35ba26c 100644
--- a/server/main.py
+++ b/server/main.py
@@ -11,16 +11,10 @@
from ArabicLabel import ArabicLabel
import arabic_reshaper
import sqlite3
-import threading;
+import threading
from kivy.clock import Clock
-class PrettyPiApp( App ):
- theme_cls = ThemeManager();
- mainBox = None;
- connection = None;
- cursor = None;
-
- kvMain = '''
+kv_config = """
#:import Toolbar kivymd.toolbar.Toolbar
#:import NavigationLayout kivymd.navigationdrawer.NavigationLayout
#:import MDNavigationDrawer kivymd.navigationdrawer.MDNavigationDrawer
@@ -33,7 +27,7 @@ class PrettyPiApp( App ):
NavigationDrawerToolbar:
title: "Navigation Drawer"
NavigationDrawerIconButton:
- id: quitBtn
+ id: quit_button
icon: 'checkbox-blank-circle'
text: "Quit"
@@ -63,57 +57,55 @@ class PrettyPiApp( App ):
name: 'mainScreen'
BoxLayout:
- id: mainBox
+ id: main_box
orientation: 'vertical'
- ''';
-
- def build( self ):
- mainWidget = Builder.load_string( self.kvMain );
-
- self.connection = sqlite3.connect( 'data.db' );
- self.cursor = self.connection.cursor();
- self.mainBox = mainWidget.ids.mainBox;
- self.quitBtn = mainWidget.ids.quitBtn;
-
- self.quitBtn.bind( on_press = lambda e: exit() );
-
- # ... #
-
- self.refreshList();
-
- # .. #
-
- Clock.schedule_interval( self.checkUpdates, 0.5 );
-
- # ... #
-
- return mainWidget;
-
- def refreshList( self ):
- self.mainBox.clear_widgets();
-
- self.cursor.execute( "SELECT * FROM TODO WHERE DONE = 'N'" );
- tasks = self.cursor.fetchall();
-
- for task in tasks:
- taskText = task[ 1 ];
-
- if task[ 5 ] == 'Y':
- taskText += " (Working On)";
-
- self.mainBox.add_widget( ArabicLabel( text = taskText, halign = 'center', font_style = 'Display1' ) );
-
- def checkUpdates( self, dt ):
- self.cursor.execute( "SELECT COUNT( 1 ) FROM UPDATE_REQUESTS WHERE UPDATE_TYPE = 'UPDATE_TODO_LIST' AND DONE = 'N'" );
-
- result = self.cursor.fetchone();
-
- if result[ 0 ] > 0:
- self.refreshList();
-
- self.cursor.execute( "UPDATE UPDATE_REQUESTS SET DONE = 'Y' WHERE UPDATE_TYPE = 'UPDATE_TODO_LIST'" );
- self.connection.commit();
-
-
-if __name__ == '__main__':
- PrettyPiApp().run();
+ """
+
+
+class PrettyPiApp(App):
+ theme_cls = ThemeManager()
+ main_box = None
+ connection = None
+ cursor = None
+ kv_main = kv_config
+
+ def build(self):
+ main_widget = Builder.load_string(self.kv_main)
+ self.connection = sqlite3.connect("data.db")
+ self.cursor = self.connection.cursor()
+ self.main_box = main_widget.ids.mainBox
+ self.quit_button = main_widget.ids.quitBtn
+ self.quit_button.bind(on_press=lambda e: exit())
+ self.refresh_list()
+ Clock.schedule_interval(self.check_updates, 0.5)
+ return main_widget
+
+ def refresh_list(self) -> None:
+ self.main_box.clear_widgets()
+ self.cursor.execute("SELECT * FROM TODO WHERE DONE = 'N'")
+ tasks = self.cursor.fetchall()
+ for task in tasks:
+ task_text = task[1]
+ if task[5] == "Y":
+ task_text += " (Working On)"
+ self.main_box.add_widget(
+ ArabicLabel(text=task_text, halign="center", font_style="Display1")
+ )
+
+ def check_updates(self) -> None:
+ self.cursor.execute(
+ "SELECT COUNT( 1 ) FROM UPDATE_REQUESTS WHERE UPDATE_TYPE = "\
+ "'UPDATE_TODO_LIST' AND DONE = 'N'"
+ )
+ result = self.cursor.fetchone()
+ if result[0] > 0:
+ self.refresh_list()
+ self.cursor.execute(
+ "UPDATE UPDATE_REQUESTS SET DONE = 'Y' WHERE UPDATE_TYPE = "\
+ "'UPDATE_TODO_LIST'"
+ )
+ self.connection.commit()
+
+
+if __name__ == "__main__":
+ PrettyPiApp().run()
diff --git a/tasks.html b/tasks.html
deleted file mode 100644
index 5b2e7d6..0000000
--- a/tasks.html
+++ /dev/null
@@ -1,115 +0,0 @@
-{% extends "base.html" %}
-
-{% block content %}
-
-
-
-
-
-
-
-
- {% if working_task != None %}
-
-
-
-
- Working On
- {{ working_task.1 }}
-
-
- {% endif %}
-
-
-
-
-
-
-
-
-
-
- | # |
- Task |
- Start |
- Done |
- Delete |
- Created On |
-
- {% for task in tasks %}
-
- | {{ loop.index }}. |
- {{ task.1 }} |
- {% if task.5 == 'N' %}
- Start |
- {% else %}
- Stop |
- {% endif %}
- Done |
- Delete |
- {{ task.2 }} |
-
- {% endfor %}
-
-
-
-
-
-
-
-
-
- | # |
- Task |
- Created On |
- Done At |
- Delete |
-
- {% for doneTask in doneTasks %}
-
- | {{ loop.index }}. |
- {{ doneTask.1 }} |
- {{ doneTask.2 }} |
- {{ doneTask.4 }} |
- Delete |
-
- {% endfor %}
-
-
-
-
-
-
-
-
-
-
-
-{% endblock %}
diff --git a/user.py b/user.py
deleted file mode 100644
index b02b178..0000000
--- a/user.py
+++ /dev/null
@@ -1,54 +0,0 @@
-from hashlib import md5
-from sqlite3 import Connection, Cursor, connect
-from typing import Optional
-
-
-class User:
- __username = Optional[str]
- __password = Optional[str]
- __name = Optional[str]
- __cursor = Optional[Cursor]
-
- @classmethod
- def get_connection(cls) -> tuple[Connection, Cursor]:
- connection = connect("data.db")
- cursor = connection.cursor()
- return connection, cursor
-
- @classmethod
- def has_permission(cls) -> bool:
- _, cursor = cls.get_connection()
- cursor.execute(
- "SELECT * FROM USERS WHERE USERNAME = ? AND PASSWORD = ?",
- (cls.__username, cls.__password),
- )
- result = cursor.fetchall()
- if len(result) > 0:
- cls.__name = result[0][3]
- return True
- return False
-
- @classmethod
- def set_username(cls, username: str) -> None:
- cls.__username = username
-
- @classmethod
- def set_password(cls, password: str) -> None:
- hashFunction = md5(password)
- cls.__password = hashFunction.hexdigest()
-
- @classmethod
- def set_hashed_password(cls, hashedPassword) -> None:
- cls.__password = hashedPassword
-
- @classmethod
- def get_username(cls) -> str:
- return cls.__username
-
- @classmethod
- def get_hashed_password(cls) -> str:
- return cls.__password
-
- @classmethod
- def get_name(cls) -> str:
- return cls.__name
From 60b27706f20feb081bbf62ccf96742b20e4cf898 Mon Sep 17 00:00:00 2001
From: Drew B <103056414+arbowl@users.noreply.github.com>
Date: Sun, 5 May 2024 09:06:05 -0400
Subject: [PATCH 3/6] Fix done_tasks variable name
---
control/templates/main_page.html | 2 +-
control/templates/tasks.html | 8 ++++----
2 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/control/templates/main_page.html b/control/templates/main_page.html
index 1df378a..d235068 100644
--- a/control/templates/main_page.html
+++ b/control/templates/main_page.html
@@ -8,12 +8,12 @@
PrettyPi
Version 0.0.1
- Click "Tasks" to view ongoing tasks
- Home
- Dashboard
+
Click "Tasks" to view ongoing tasks
diff --git a/control/templates/tasks.html b/control/templates/tasks.html
index 5b2e7d6..6953a41 100644
--- a/control/templates/tasks.html
+++ b/control/templates/tasks.html
@@ -92,12 +92,12 @@
Done!
Done At |
Delete |
- {% for doneTask in doneTasks %}
+ {% for done_task in done_tasks %}
| {{ loop.index }}. |
- {{ doneTask.1 }} |
- {{ doneTask.2 }} |
- {{ doneTask.4 }} |
+ {{ done_task.1 }} |
+ {{ done_task.2 }} |
+ {{ done_task.4 }} |
Delete |
{% endfor %}
From 92ca381af397c0bd2c78ca62ba88578d88ff5643 Mon Sep 17 00:00:00 2001
From: arbowl
Date: Sun, 5 May 2024 10:05:27 -0400
Subject: [PATCH 4/6] Satisfy pylint requirements
---
control/__init__.py | 0
control/app.py | 51 ++++++++++++++++++++++++--------
control/templates/main_page.html | 7 ++---
control/user.py | 24 +++++++++++----
server/ArabicLabel.py | 17 -----------
server/__init__.py | 0
server/arabic_label.py | 26 ++++++++++++++++
server/main.py | 43 +++++++++++++++++----------
8 files changed, 113 insertions(+), 55 deletions(-)
create mode 100644 control/__init__.py
delete mode 100644 server/ArabicLabel.py
create mode 100644 server/__init__.py
create mode 100644 server/arabic_label.py
diff --git a/control/__init__.py b/control/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/control/app.py b/control/app.py
index ff12757..957dace 100644
--- a/control/app.py
+++ b/control/app.py
@@ -1,27 +1,42 @@
-from flask import Flask, render_template, request, make_response, redirect, Response
+"""App
+
+Handles the I/O and database queries
+"""
+
from hashlib import md5
from sqlite3 import connect, Connection, Cursor
from time import strftime
from typing import Optional
-from user import User
+
+from flask import Flask, render_template, request, make_response, redirect, Response
+
+from control.user import User
app = Flask(__name__)
+TASK_ID_IS_NONE_ERROR =(
+ "Error: No task associated with that ID. Maybe someone else already deleted it?"
+)
+
+
def get_connection() -> tuple[Connection, Cursor]:
+ """Returns the SQLite"""
connection = connect("data.db")
cursor = connection.cursor()
return connection, cursor
def is_installed() -> bool:
+ """Returns True if a user already exists"""
_, cursor = get_connection()
cursor.execute("SELECT COUNT( 1 ) FROM USERS")
has_user = cursor.fetchone()[0]
return has_user > 0
-def add_request(request_type) -> None:
+def add_request(request_type: str) -> None:
+ """Adds a task to the DB"""
connection, cursor = get_connection()
creation_date = strftime("%d-%m-%Y %H:%M:%S")
cursor.execute(
@@ -34,6 +49,7 @@ def add_request(request_type) -> None:
@app.before_request
def before_request() -> Optional[str]:
+ """Determines which page to show the user based on install and login status"""
if request.path == "/install":
return None
if not is_installed():
@@ -49,11 +65,13 @@ def before_request() -> Optional[str]:
@app.route("/")
def main() -> str:
+ """This is the landing page"""
return render_template("main_page.html", name=User.get_name())
@app.route("/install", methods=["POST"])
def install() -> str:
+ """Initializes the PrettyPi site for the network"""
connection, cursor = get_connection()
if is_installed():
return render_template(
@@ -78,14 +96,15 @@ def install() -> str:
connection.commit()
return render_template(
"installer_message.html",
- message="Congratulations! PrettyPi has been initialized. "\
+ message="Congratulations! PrettyPi has been initialized. "
"Go back to homepage to start using it",
type="success",
)
@app.route("/login", methods=["POST"])
-def login() -> str:
+def login() -> Response | str:
+ """Determines if the login information is valid"""
User.set_username(request.form["username"])
User.set_password(request.form["password"].encode("utf-8"))
if User.has_permission():
@@ -93,11 +112,12 @@ def login() -> str:
response.set_cookie("prettypi_username", User.get_username())
response.set_cookie("prettypi_password", User.get_hashed_password())
return response
- return "Invalid"
+ return f'Invalid username ("{User.get_username()}") or password'
@app.route("/logout")
def logout() -> Response:
+ """Logs the current user out"""
response = make_response(redirect("/"))
response.set_cookie("prettypi_username", "")
response.set_cookie("prettypi_password", "")
@@ -106,6 +126,7 @@ def logout() -> Response:
@app.route("/tasks")
def tasks_main():
+ """Retrieves the completed, running, and done tasks from the DB to display"""
_, cursor = get_connection()
cursor.execute("SELECT * FROM TODO WHERE DONE = 'N' ORDER BY CREATION_DATE DESC")
tasks = cursor.fetchall()
@@ -124,6 +145,7 @@ def tasks_main():
@app.route("/add_task", methods=["POST"])
def new_task():
+ """Adds a new task to the DB"""
connection, cursor = get_connection()
if not request.form["task_details"]:
return render_template(
@@ -141,10 +163,11 @@ def new_task():
@app.route("/delete_task", methods=["GET"])
def delete_task() -> Response | str:
+ """Deletes"""
connection, cursor = get_connection()
task_id = request.args.get("task_id", None)
if task_id is None:
- return "Invalid"
+ return TASK_ID_IS_NONE_ERROR
cursor.execute("DELETE FROM TODO WHERE TASK_ID = ?", [task_id])
cursor.execute("DELETE FROM TASKS_LOG WHERE TASK_ID = ?", [task_id])
connection.commit()
@@ -154,10 +177,11 @@ def delete_task() -> Response | str:
@app.route("/task_done", methods=["GET"])
def mark_task_as_done() -> Response | str:
+ """Move a task to the "Done" table"""
connection, cursor = get_connection()
task_id = request.args.get("task_id", None)
if task_id is None:
- return "Invalid"
+ return TASK_ID_IS_NONE_ERROR
current_date = strftime("%d-%m-%Y %H:%M:%S")
cursor.execute(
"UPDATE TODO SET WORKING_ON = 'N', DONE = 'Y', DONE_AT = ? WHERE TASK_ID = ?",
@@ -170,13 +194,13 @@ def mark_task_as_done() -> Response | str:
@app.route("/start_task", methods=["GET"])
def start_working_on_task() -> Response | str:
+ """Create the banner to indicate a task is currently pending"""
connection, cursor = get_connection()
task_id = request.args.get("task_id", None)
if task_id is None:
- return "Invalid"
+ return TASK_ID_IS_NONE_ERROR
cursor.execute(
- "SELECT COUNT( 1 ) FROM TODO WHERE WORKING_ON = 'Y' AND TASK_ID <> ?",
- [task_id]
+ "SELECT COUNT( 1 ) FROM TODO WHERE WORKING_ON = 'Y' AND TASK_ID <> ?", [task_id]
)
currently_working_on = cursor.fetchone()[0]
if currently_working_on > 0:
@@ -186,7 +210,7 @@ def start_working_on_task() -> Response | str:
type="warning",
)
creation_date = strftime("%d-%m-%Y %H:%M:%S")
- cursor.execute("UPDATE TODO SET WORKING_ON = 'Y' WHERE TASK_ID = ?",[task_id])
+ cursor.execute("UPDATE TODO SET WORKING_ON = 'Y' WHERE TASK_ID = ?", [task_id])
cursor.execute(
"INSERT INTO TASKS_LOG( TASK_ID, START_AT ) VALUES ( ?, ? )",
[task_id, creation_date],
@@ -198,10 +222,11 @@ def start_working_on_task() -> Response | str:
@app.route("/stop_task", methods=["GET"])
def stop_working_on_task() -> Response | str:
+ """Remove a pending task from the banner"""
connection, cursor = get_connection()
task_id = request.args.get("task_id", None)
if task_id is None:
- return "Invalid"
+ return TASK_ID_IS_NONE_ERROR
cursor.execute(
"SELECT MAX( log_id ) FROM tasks_log WHERE TASK_ID = ? AND ENDED_AT IS NULL",
[task_id],
diff --git a/control/templates/main_page.html b/control/templates/main_page.html
index d235068..1b64b0a 100644
--- a/control/templates/main_page.html
+++ b/control/templates/main_page.html
@@ -5,10 +5,9 @@