diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9a787e3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +*.mo +*.vo +*.pyc +venv diff --git a/file-explorer/file-explorer.py b/file-explorer/file-explorer.py new file mode 100644 index 0000000..0f5cf6b --- /dev/null +++ b/file-explorer/file-explorer.py @@ -0,0 +1,37 @@ +import tkinter as tk +from tkinter import filedialog + +def browse_file(): + file_path = filedialog.askopenfilename() + if file_path: + selected_file.set(file_path) + +def browse_directory(): + directory_path = filedialog.askdirectory() + if directory_path: + selected_directory.set(directory_path) + +# Create the main window +root = tk.Tk() +root.title("Cool File Explorer") + +# Create a label to display the selected file +selected_file = tk.StringVar() +file_label = tk.Label(root, textvariable=selected_file, font=('Helvetica', 12)) +file_label.pack(pady=10) + +# Create a button to browse for a file +file_button = tk.Button(root, text="Browse File", font=('Helvetica', 12), command=browse_file) +file_button.pack() + +# Create a label to display the selected directory +selected_directory = tk.StringVar() +directory_label = tk.Label(root, textvariable=selected_directory, font=('Helvetica', 12)) +directory_label.pack(pady=10) + +# Create a button to browse for a directory +directory_button = tk.Button(root, text="Browse Directory", font=('Helvetica', 12), command=browse_directory) +directory_button.pack() + +# Start the GUI main loop +root.mainloop() diff --git a/file-organizing/file_organizing.py b/file-organizing/file_organizing.py new file mode 100644 index 0000000..da71cc9 --- /dev/null +++ b/file-organizing/file_organizing.py @@ -0,0 +1,49 @@ +# file handling: navigate, rename, move, copy, remove +import os +import shutil +from pathlib import Path + +# change working directory +print(os.getcwd()) + +os.chdir("/Users/patrick/Desktop/video-files") +print(os.getcwd()) + +# rename files +for file in os.listdir(): + # This example changes filenames from + # 'dictionary - python-course-3.mov' + # to --> + # '03-python-course-dictionary.mov' + name, ext = os.path.splitext(file) + + splitted = name.split("-") + splitted = [s.strip() for s in splitted] + new_name = f"{splitted[3].zfill(2)}-{splitted[1]}-{splitted[2]}-{splitted[0]}{ext}" + + os.rename(file, new_name) + + # or + # f = Path(file) + # name, ext = f.stem, f.suffix + # f.rename(new_name) + +# create directory +Path("data").mkdir(exist_ok=True) + +# or +if not os.path.exists("data"): + os.mkdir("data") + +# move file and folder +shutil.move('f', 'd') # works for file and folder + +# copy file and folder +shutil.copy("src", "dest") +shutil.copy2("src", "dest") + +# remove file and folder +os.remove("filename") # error if not found +os.rmdir("folder") # error if not empty, or not found +shutil.rmtree("folder") # works for non empty directories + diff --git a/file-organizing/organize-desktop.py b/file-organizing/organize-desktop.py new file mode 100644 index 0000000..0f0c85e --- /dev/null +++ b/file-organizing/organize-desktop.py @@ -0,0 +1,47 @@ +# organize the desktop +# moves images, videos, screenshots, and audio files +# into corresponding folders +import os +import shutil + + +audio = (".3ga", ".aac", ".ac3", ".aif", ".aiff", + ".alac", ".amr", ".ape", ".au", ".dss", + ".flac", ".flv", ".m4a", ".m4b", ".m4p", + ".mp3", ".mpga", ".ogg", ".oga", ".mogg", + ".opus", ".qcp", ".tta", ".voc", ".wav", + ".wma", ".wv") + +video = (".webm", ".MTS", ".M2TS", ".TS", ".mov", + ".mp4", ".m4p", ".m4v", ".mxf") + +img = (".jpg", ".jpeg", ".jfif", ".pjpeg", ".pjp", ".png", + ".gif", ".webp", ".svg", ".apng", ".avif") + +def is_audio(file): + return os.path.splitext(file)[1] in audio + +def is_video(file): + return os.path.splitext(file)[1] in video + +def is_image(file): + return os.path.splitext(file)[1] in img + +def is_screenshot(file): + name, ext = os.path.splitext(file) + return (ext in img) and "screenshot" in name.lower() + +os.chdir("/Users/patrick/Desktop") + +for file in os.listdir(): + if is_audio(file): + shutil.move(file, "Users/patrick/Documents/audio") + elif is_video(file): + shutil.move(file, "Users/patrick/Documents/video") + elif is_image(file): + if is_screenshot(file): + shutil.move(file, "Users/patrick/Documents/screenshots") + else: + shutil.move(file, "Users/patrick/Documents/images") + else: + shutil.move(file, "Users/patrick/Documents") diff --git a/image-viewer/image-viewer.py b/image-viewer/image-viewer.py new file mode 100644 index 0000000..9419dca --- /dev/null +++ b/image-viewer/image-viewer.py @@ -0,0 +1,26 @@ +import tkinter as tk +from tkinter import filedialog +from PIL import Image, ImageTk + +def open_image(): + file_path = filedialog.askopenfilename(filetypes=[("Image files", "*.png;*.jpg;*.jpeg;*.gif;*.bmp")]) + if file_path: + image = Image.open(file_path) + photo = ImageTk.PhotoImage(image) + label.config(image=photo) + label.image = photo + +# Create the main window +root = tk.Tk() +root.title("Cool Image Viewer") + +# Create a label to display the image +label = tk.Label(root) +label.pack() + +# Create a button to open an image +open_button = tk.Button(root, text="Open Image", font=('Helvetica', 14), command=open_image) +open_button.pack() + +# Start the GUI main loop +root.mainloop() diff --git a/note-take/note-take.py b/note-take/note-take.py new file mode 100644 index 0000000..ec7b7b2 --- /dev/null +++ b/note-take/note-take.py @@ -0,0 +1,38 @@ +import tkinter as tk +from tkinter import scrolledtext +from tkinter import filedialog + +def save_note(): + note = text_widget.get("1.0", "end-1c") # Get text from the text widget + file_path = filedialog.asksaveasfilename(defaultextension=".txt", filetypes=[("Text files", "*.txt")]) + + if file_path: + with open(file_path, 'w') as file: + file.write(note) + +def open_note(): + file_path = filedialog.askopenfilename(filetypes=[("Text files", "*.txt")]) + + if file_path: + with open(file_path, 'r') as file: + note = file.read() + text_widget.delete("1.0", "end") # Clear existing text + text_widget.insert("1.0", note) # Insert the loaded note + +# Create the main window +root = tk.Tk() +root.title("Cool Note Taker") + +# Create a scrolled text widget +text_widget = scrolledtext.ScrolledText(root, font=('Helvetica', 14)) +text_widget.pack(expand=True, fill='both') + +# Create "Save" and "Open" buttons +save_button = tk.Button(root, text="Save Note", font=('Helvetica', 12), command=save_note) +save_button.pack(side="left", padx=10, pady=10) + +open_button = tk.Button(root, text="Open Note", font=('Helvetica', 12), command=open_note) +open_button.pack(side="right", padx=10, pady=10) + +# Start the GUI main loop +root.mainloop() diff --git a/paint/paint.py b/paint/paint.py new file mode 100644 index 0000000..efc7ae3 --- /dev/null +++ b/paint/paint.py @@ -0,0 +1,38 @@ +import tkinter as tk + +class PaintApp: + def __init__(self, root): + self.root = root + self.root.title("Cool Paint App") + + self.canvas = tk.Canvas(root, bg="white") + self.canvas.pack(fill=tk.BOTH, expand=True) + + self.button_clear = tk.Button(root, text="Clear", command=self.clear_canvas) + self.button_clear.pack() + + self.canvas.bind("", self.start_paint) + self.canvas.bind("", self.paint) + + self.old_x = None + self.old_y = None + + def start_paint(self, event): + self.old_x = event.x + self.old_y = event.y + + def paint(self, event): + new_x = event.x + new_y = event.y + if self.old_x and self.old_y: + self.canvas.create_line(self.old_x, self.old_y, new_x, new_y, fill="black", width=2) + self.old_x = new_x + self.old_y = new_y + + def clear_canvas(self): + self.canvas.delete("all") + +if __name__ == "__main__": + root = tk.Tk() + app = PaintApp(root) + root.mainloop() diff --git a/photo-restoration/.env b/photo-restoration/.env new file mode 100644 index 0000000..2d281eb --- /dev/null +++ b/photo-restoration/.env @@ -0,0 +1 @@ +REPLICATE_API_TOKEN=YOUR_TOKEN_HERE \ No newline at end of file diff --git a/photo-restoration/README.md b/photo-restoration/README.md new file mode 100644 index 0000000..9160f6f --- /dev/null +++ b/photo-restoration/README.md @@ -0,0 +1,22 @@ +# Flask app to restore photos + +Simple Flask app to restore old photos with AI. It uses the [GFPGAN](https://replicate.com/tencentarc/gfpgan) model on [Replicate](https://replicate.com/). + +![Screenshot](screenshot.png) +## Setup +```bash +pip install flask replicate python-dotenv +``` + +You need a [Replicate](https://replicate.com/) API Token (You can get started for free). Put the token in the `.env` file. + +Then start the app, upload a photo, and have fun! + +```bash +python main.py +``` + +## Resources + +- Inspired by [restorephotos.io](https://www.restorephotos.io/) +- [https://github.com/TencentARC/GFPGAN](https://github.com/TencentARC/GFPGAN) \ No newline at end of file diff --git a/photo-restoration/main.py b/photo-restoration/main.py new file mode 100644 index 0000000..84d9d41 --- /dev/null +++ b/photo-restoration/main.py @@ -0,0 +1,42 @@ +from flask import Flask, request, redirect, url_for, render_template +from werkzeug.utils import secure_filename +from photo_restorer import predict_image + +UPLOAD_FOLDER = '/static/images/' +ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg'} + +app = Flask(__name__) +app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER +app.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024 + + +def allowed_file(filename): + return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS + +@app.route('/') +def home(): + return render_template('index.html') + + +@app.route('/', methods=['POST']) +def upload_image(): + if 'file' not in request.files: + return redirect(request.url) + + file = request.files['file'] + if file.filename == '': + return redirect(request.url) + + if file and allowed_file(file.filename): + filename = secure_filename(file.filename) + full_filepath = "." + url_for('static', filename='images/' + filename) + + file.save(full_filepath) + restored_img_url = predict_image(full_filepath) + return render_template('index.html', filename=filename, restored_img_url=restored_img_url) + else: + return redirect(request.url) + + +if __name__ == "__main__": + app.run(debug=True) \ No newline at end of file diff --git a/photo-restoration/photo_restorer.py b/photo-restoration/photo_restorer.py new file mode 100644 index 0000000..dacdb11 --- /dev/null +++ b/photo-restoration/photo_restorer.py @@ -0,0 +1,27 @@ +from dotenv import load_dotenv +load_dotenv() + +import replicate + +model = replicate.models.get("tencentarc/gfpgan") +version = model.versions.get("9283608cc6b7be6b65a8e44983db012355fde4132009bf99d976b2f0896856a3") + +def predict_image(filename): + # https://replicate.com/tencentarc/gfpgan/versions/9283608cc6b7be6b65a8e44983db012355fde4132009bf99d976b2f0896856a3#input + inputs = { + # Input + 'img': open(filename, "rb"), + + # GFPGAN version. v1.3: better quality. v1.4: more details and better + # identity. + 'version': "v1.4", + + # Rescaling factor + 'scale': 2, + } + print(inputs) + + # https://replicate.com/tencentarc/gfpgan/versions/9283608cc6b7be6b65a8e44983db012355fde4132009bf99d976b2f0896856a3#output-schema + output = version.predict(**inputs) + print(output) + return output \ No newline at end of file diff --git a/photo-restoration/screenshot.png b/photo-restoration/screenshot.png new file mode 100644 index 0000000..2f4acce Binary files /dev/null and b/photo-restoration/screenshot.png differ diff --git a/photo-restoration/static/images/example.jpeg b/photo-restoration/static/images/example.jpeg new file mode 100644 index 0000000..ebae03a Binary files /dev/null and b/photo-restoration/static/images/example.jpeg differ diff --git a/photo-restoration/templates/index.html b/photo-restoration/templates/index.html new file mode 100644 index 0000000..c2cecfe --- /dev/null +++ b/photo-restoration/templates/index.html @@ -0,0 +1,36 @@ + + + + + + + Document + + +

Photo Restoration

+ +{% if filename %} +
+

Original Image:

+ +
+{% endif %} +{% if restored_img_url %} +
+

Restored Image:

+ + +
+{% endif %} + +
+

+ +

+

+ +

+
+ + + \ No newline at end of file diff --git a/stopwatch/stopwatch.py b/stopwatch/stopwatch.py new file mode 100644 index 0000000..8586859 --- /dev/null +++ b/stopwatch/stopwatch.py @@ -0,0 +1,53 @@ +import tkinter as tk +import time + +def start(): + global running + running = True + start_button['state'] = 'disabled' + stop_button['state'] = 'active' + update() + +def stop(): + global running + running = False + start_button['state'] = 'active' + stop_button['state'] = 'disabled' + +def reset(): + global running, elapsed_time + running = False + elapsed_time = 0 + time_label.config(text="00:00:00") + start_button['state'] = 'active' + stop_button['state'] = 'disabled' + +def update(): + if running: + global elapsed_time + elapsed_time += 1 + hours, remainder = divmod(elapsed_time, 3600) + minutes, seconds = divmod(remainder, 60) + time_str = f"{hours:02d}:{minutes:02d}:{seconds:02d}" + time_label.config(text=time_str) + time_label.after(1000, update) + +running = False +elapsed_time = 0 + +root = tk.Tk() +root.title("Cool Stopwatch") + +time_label = tk.Label(root, text="00:00:00", font=('Helvetica', 48)) +time_label.pack(padx=20, pady=20) + +start_button = tk.Button(root, text="Start", font=('Helvetica', 16), command=start) +start_button.pack(side="left", padx=10) +stop_button = tk.Button(root, text="Stop", font=('Helvetica', 16), command=stop) +stop_button.pack(side="left", padx=10) +reset_button = tk.Button(root, text="Reset", font=('Helvetica', 16), command=reset) +reset_button.pack(side="left", padx=10) + +stop_button['state'] = 'disabled' + +root.mainloop() diff --git a/text-editor/text-editor.py b/text-editor/text-editor.py new file mode 100644 index 0000000..3194bb6 --- /dev/null +++ b/text-editor/text-editor.py @@ -0,0 +1,40 @@ +import tkinter as tk +from tkinter import filedialog, scrolledtext + +def open_file(): + file_path = filedialog.askopenfilename(filetypes=[("Text files", "*.txt")]) + if file_path: + with open(file_path, 'r') as file: + text.delete(1.0, tk.END) + text.insert(tk.END, file.read()) + root.title(f"Cool Text Editor - {file_path}") + +def save_file(): + file_path = filedialog.asksaveasfilename(defaultextension=".txt", filetypes=[("Text files", "*.txt")]) + if file_path: + with open(file_path, 'w') as file: + file.write(text.get(1.0, tk.END)) + root.title(f"Cool Text Editor - {file_path}") + +# Create the main window +root = tk.Tk() +root.title("Cool Text Editor") + +# Create a scrolled text widget +text = scrolledtext.ScrolledText(root, font=('Helvetica', 14)) +text.pack(expand=True, fill='both') + +# Create the menu bar +menu_bar = tk.Menu(root) +root.config(menu=menu_bar) + +# File menu +file_menu = tk.Menu(menu_bar) +menu_bar.add_cascade(label="File", menu=file_menu) +file_menu.add_command(label="Open", command=open_file) +file_menu.add_command(label="Save", command=save_file) +file_menu.add_separator() +file_menu.add_command(label="Exit", command=root.quit) + +# Start the GUI main loop +root.mainloop() diff --git a/to-do/to-do.py b/to-do/to-do.py new file mode 100644 index 0000000..291eb75 --- /dev/null +++ b/to-do/to-do.py @@ -0,0 +1,35 @@ +import tkinter as tk + +def add_task(): + task = entry.get() + if task: + listbox.insert(tk.END, task) + entry.delete(0, tk.END) + +def remove_task(): + selected_task = listbox.curselection() + if selected_task: + listbox.delete(selected_task) + +# Create the main window +root = tk.Tk() +root.title("Cool To-Do List") + +# Entry widget for adding tasks +entry = tk.Entry(root, font=('Helvetica', 18)) +entry.grid(row=0, column=0, columnspan=2) + +# Button to add tasks +add_button = tk.Button(root, text="Add", font=('Helvetica', 14), command=add_task) +add_button.grid(row=0, column=2) + +# Button to remove tasks +remove_button = tk.Button(root, text="Remove", font=('Helvetica', 14), command=remove_task) +remove_button.grid(row=0, column=3) + +# Listbox to display tasks +listbox = tk.Listbox(root, font=('Helvetica', 18), selectmode=tk.SINGLE, selectbackground="#a5a5a5") +listbox.grid(row=1, column=0, columnspan=4) + +# Start the GUI main loop +root.mainloop() diff --git a/todocli-tutorial/database.py b/todocli-tutorial/database.py new file mode 100644 index 0000000..5dd9d50 --- /dev/null +++ b/todocli-tutorial/database.py @@ -0,0 +1,76 @@ +import sqlite3 +from typing import List +import datetime +from model import Todo + +conn = sqlite3.connect('todos.db') +c = conn.cursor() + + +def create_table(): + c.execute("""CREATE TABLE IF NOT EXISTS todos ( + task text, + category text, + date_added text, + date_completed text, + status integer, + position integer + )""") + + +create_table() + + +def insert_todo(todo: Todo): + c.execute('select count(*) FROM todos') + count = c.fetchone()[0] + todo.position = count if count else 0 + with conn: + c.execute('INSERT INTO todos VALUES (:task, :category, :date_added, :date_completed, :status, :position)', + {'task': todo.task, 'category': todo.category, 'date_added': todo.date_added, + 'date_completed': todo.date_completed, 'status': todo.status, 'position': todo.position }) + + +def get_all_todos() -> List[Todo]: + c.execute('select * from todos') + results = c.fetchall() + todos = [] + for result in results: + todos.append(Todo(*result)) + return todos + + +def delete_todo(position): + c.execute('select count(*) from todos') + count = c.fetchone()[0] + + with conn: + c.execute("DELETE from todos WHERE position=:position", {"position": position}) + for pos in range(position+1, count): + change_position(pos, pos-1, False) + + +def change_position(old_position: int, new_position: int, commit=True): + c.execute('UPDATE todos SET position = :position_new WHERE position = :position_old', + {'position_old': old_position, 'position_new': new_position}) + if commit: + conn.commit() + + +def update_todo(position: int, task: str, category: str): + with conn: + if task is not None and category is not None: + c.execute('UPDATE todos SET task = :task, category = :category WHERE position = :position', + {'position': position, 'task': task, 'category': category}) + elif task is not None: + c.execute('UPDATE todos SET task = :task WHERE position = :position', + {'position': position, 'task': task}) + elif category is not None: + c.execute('UPDATE todos SET category = :category WHERE position = :position', + {'position': position, 'category': category}) + + +def complete_todo(position: int): + with conn: + c.execute('UPDATE todos SET status = 2, date_completed = :date_completed WHERE position = :position', + {'position': position, 'date_completed': datetime.datetime.now().isoformat()}) diff --git a/todocli-tutorial/model.py b/todocli-tutorial/model.py new file mode 100644 index 0000000..c00fc99 --- /dev/null +++ b/todocli-tutorial/model.py @@ -0,0 +1,16 @@ +import datetime + + +class Todo: + def __init__(self, task, category, + date_added=None, date_completed=None, + status=None, position=None): + self.task = task + self.category = category + self.date_added = date_added if date_added is not None else datetime.datetime.now().isoformat() + self.date_completed = date_completed if date_completed is not None else None + self.status = status if status is not None else 1 # 1 = open, 2 = completed + self.position = position if position is not None else None + + def __repr__(self) -> str: + return f"({self.task}, {self.category}, {self.date_added}, {self.date_completed}, {self.status}, {self.position})" \ No newline at end of file diff --git a/todocli-tutorial/todocli.py b/todocli-tutorial/todocli.py new file mode 100644 index 0000000..739399f --- /dev/null +++ b/todocli-tutorial/todocli.py @@ -0,0 +1,63 @@ +import typer +from rich.console import Console +from rich.table import Table +from model import Todo +from database import get_all_todos, delete_todo, insert_todo, complete_todo, update_todo + +console = Console() + +app = typer.Typer() + + +@app.command(short_help='adds an item') +def add(task: str, category: str): + typer.echo(f"adding {task}, {category}") + todo = Todo(task, category) + insert_todo(todo) + show() + +@app.command() +def delete(position: int): + typer.echo(f"deleting {position}") + # indices in UI begin at 1, but in database at 0 + delete_todo(position-1) + show() + +@app.command() +def update(position: int, task: str = None, category: str = None): + typer.echo(f"updating {position}") + update_todo(position-1, task, category) + show() + +@app.command() +def complete(position: int): + typer.echo(f"complete {position}") + complete_todo(position-1) + show() + +@app.command() +def show(): + tasks = get_all_todos() + console.print("[bold magenta]Todos[/bold magenta]!", "💻") + + table = Table(show_header=True, header_style="bold blue") + table.add_column("#", style="dim", width=6) + table.add_column("Todo", min_width=20) + table.add_column("Category", min_width=12, justify="right") + table.add_column("Done", min_width=12, justify="right") + + def get_category_color(category): + COLORS = {'Learn': 'cyan', 'YouTube': 'red', 'Sports': 'cyan', 'Study': 'green'} + if category in COLORS: + return COLORS[category] + return 'white' + + for idx, task in enumerate(tasks, start=1): + c = get_category_color(task.category) + is_done_str = '✅' if task.status == 2 else '❌' + table.add_row(str(idx), task.task, f'[{c}]{task.category}[/{c}]', is_done_str) + console.print(table) + + +if __name__ == "__main__": + app() \ No newline at end of file diff --git a/webapps/django/README.md b/webapps/django/README.md new file mode 100755 index 0000000..3344693 --- /dev/null +++ b/webapps/django/README.md @@ -0,0 +1,47 @@ +## Steps: + +### Installation + +```console +pip install Django +django-admin startproject todoapp +``` + +### Start + +```console +python manage.py migrate +python manage.py runserver +python manage.py startapp todolist +``` + +- add 'todolist' to INSTALLED_APPS + +### Add views +- implement todolist.views.py and create todolist.urls.py +- add urls to todoapp.urls.py + +### Add templates +- add templates folder and file +- add "templates" to DIR in settings.py +- modify view: return render... + +### Add models +- implement todolist.models.py + +### Put together +```console +manage.py makemigrations +python manage.py migrate +python manage.py createsuperuser +``` + +- Adding models to the administration site: + - todolist.admin.py: admin.site.register(Todo) +- login to admin + +### add template +- add {% csrf_token %} to template + +### CRUD +- implement views \ No newline at end of file diff --git a/webapps/django/todoapp/manage.py b/webapps/django/todoapp/manage.py new file mode 100755 index 0000000..154423c --- /dev/null +++ b/webapps/django/todoapp/manage.py @@ -0,0 +1,22 @@ +#!/usr/bin/env python +"""Django's command-line utility for administrative tasks.""" +import os +import sys + + +def main(): + """Run administrative tasks.""" + os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'todoapp.settings') + try: + from django.core.management import execute_from_command_line + except ImportError as exc: + raise ImportError( + "Couldn't import Django. Are you sure it's installed and " + "available on your PYTHONPATH environment variable? Did you " + "forget to activate a virtual environment?" + ) from exc + execute_from_command_line(sys.argv) + + +if __name__ == '__main__': + main() diff --git a/webapps/django/todoapp/templates/base.html b/webapps/django/todoapp/templates/base.html new file mode 100755 index 0000000..534f870 --- /dev/null +++ b/webapps/django/todoapp/templates/base.html @@ -0,0 +1,45 @@ + + + + + + + Todo App + + + + + + +
+

To Do App

+ +
+ {% csrf_token %} +
+ +
+
+ +
+ +
+ + {% for todo in todo_list %} +
+

{{ todo.id }} | {{ todo.title }}

+ + {% if todo.complete == False %} + Not Complete + {% else %} + Completed + {% endif %} + + Update + Delete +
+ {% endfor %} +
+ + + \ No newline at end of file diff --git a/webapps/django/todoapp/todoapp/__init__.py b/webapps/django/todoapp/todoapp/__init__.py new file mode 100755 index 0000000..e69de29 diff --git a/webapps/django/todoapp/todoapp/asgi.py b/webapps/django/todoapp/todoapp/asgi.py new file mode 100755 index 0000000..94765bc --- /dev/null +++ b/webapps/django/todoapp/todoapp/asgi.py @@ -0,0 +1,16 @@ +""" +ASGI config for todoapp project. + +It exposes the ASGI callable as a module-level variable named ``application``. + +For more information on this file, see +https://docs.djangoproject.com/en/4.0/howto/deployment/asgi/ +""" + +import os + +from django.core.asgi import get_asgi_application + +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'todoapp.settings') + +application = get_asgi_application() diff --git a/webapps/django/todoapp/todoapp/settings.py b/webapps/django/todoapp/todoapp/settings.py new file mode 100755 index 0000000..43afc09 --- /dev/null +++ b/webapps/django/todoapp/todoapp/settings.py @@ -0,0 +1,124 @@ +""" +Django settings for todoapp project. + +Generated by 'django-admin startproject' using Django 4.0.1. + +For more information on this file, see +https://docs.djangoproject.com/en/4.0/topics/settings/ + +For the full list of settings and their values, see +https://docs.djangoproject.com/en/4.0/ref/settings/ +""" + +from pathlib import Path + +# Build paths inside the project like this: BASE_DIR / 'subdir'. +BASE_DIR = Path(__file__).resolve().parent.parent + + +# Quick-start development settings - unsuitable for production +# See https://docs.djangoproject.com/en/4.0/howto/deployment/checklist/ + +# SECURITY WARNING: keep the secret key used in production secret! +SECRET_KEY = 'django-insecure-8up6eur7!aw%w+h_w5!i)=)k1#!lclxbq6@u!5x5z$gwju-$*1' + +# SECURITY WARNING: don't run with debug turned on in production! +DEBUG = True + +ALLOWED_HOSTS = [] + + +# Application definition + +INSTALLED_APPS = [ + 'django.contrib.admin', + 'django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'django.contrib.messages', + 'django.contrib.staticfiles', + 'todolist' +] + +MIDDLEWARE = [ + 'django.middleware.security.SecurityMiddleware', + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.middleware.common.CommonMiddleware', + 'django.middleware.csrf.CsrfViewMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'django.contrib.messages.middleware.MessageMiddleware', + 'django.middleware.clickjacking.XFrameOptionsMiddleware', +] + +ROOT_URLCONF = 'todoapp.urls' + +TEMPLATES = [ + { + 'BACKEND': 'django.template.backends.django.DjangoTemplates', + 'DIRS': ["templates"], + 'APP_DIRS': True, + 'OPTIONS': { + 'context_processors': [ + 'django.template.context_processors.debug', + 'django.template.context_processors.request', + 'django.contrib.auth.context_processors.auth', + 'django.contrib.messages.context_processors.messages', + ], + }, + }, +] + +WSGI_APPLICATION = 'todoapp.wsgi.application' + + +# Database +# https://docs.djangoproject.com/en/4.0/ref/settings/#databases + +DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.sqlite3', + 'NAME': BASE_DIR / 'db.sqlite3', + } +} + + +# Password validation +# https://docs.djangoproject.com/en/4.0/ref/settings/#auth-password-validators + +AUTH_PASSWORD_VALIDATORS = [ + { + 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', + }, +] + + +# Internationalization +# https://docs.djangoproject.com/en/4.0/topics/i18n/ + +LANGUAGE_CODE = 'en-us' + +TIME_ZONE = 'UTC' + +USE_I18N = True + +USE_TZ = True + + +# Static files (CSS, JavaScript, Images) +# https://docs.djangoproject.com/en/4.0/howto/static-files/ + +STATIC_URL = 'static/' + +# Default primary key field type +# https://docs.djangoproject.com/en/4.0/ref/settings/#default-auto-field + +DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' diff --git a/webapps/django/todoapp/todoapp/urls.py b/webapps/django/todoapp/todoapp/urls.py new file mode 100755 index 0000000..c8f5422 --- /dev/null +++ b/webapps/django/todoapp/todoapp/urls.py @@ -0,0 +1,23 @@ +"""todoapp URL Configuration + +The `urlpatterns` list routes URLs to views. For more information please see: + https://docs.djangoproject.com/en/4.0/topics/http/urls/ +Examples: +Function views + 1. Add an import: from my_app import views + 2. Add a URL to urlpatterns: path('', views.home, name='home') +Class-based views + 1. Add an import: from other_app.views import Home + 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') +Including another URLconf + 1. Import the include() function: from django.urls import include, path + 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) +""" +from django.contrib import admin +from django.urls import path, include + + +urlpatterns = [ + path('admin/', admin.site.urls), + path('', include('todolist.urls')), +] diff --git a/webapps/django/todoapp/todoapp/wsgi.py b/webapps/django/todoapp/todoapp/wsgi.py new file mode 100755 index 0000000..b83347e --- /dev/null +++ b/webapps/django/todoapp/todoapp/wsgi.py @@ -0,0 +1,16 @@ +""" +WSGI config for todoapp project. + +It exposes the WSGI callable as a module-level variable named ``application``. + +For more information on this file, see +https://docs.djangoproject.com/en/4.0/howto/deployment/wsgi/ +""" + +import os + +from django.core.wsgi import get_wsgi_application + +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'todoapp.settings') + +application = get_wsgi_application() diff --git a/webapps/django/todoapp/todolist/__init__.py b/webapps/django/todoapp/todolist/__init__.py new file mode 100755 index 0000000..e69de29 diff --git a/webapps/django/todoapp/todolist/admin.py b/webapps/django/todoapp/todolist/admin.py new file mode 100755 index 0000000..6d5cc0c --- /dev/null +++ b/webapps/django/todoapp/todolist/admin.py @@ -0,0 +1,5 @@ +from django.contrib import admin +from .models import Todo + +# Register your models here. +admin.site.register(Todo) \ No newline at end of file diff --git a/webapps/django/todoapp/todolist/apps.py b/webapps/django/todoapp/todolist/apps.py new file mode 100755 index 0000000..85a91be --- /dev/null +++ b/webapps/django/todoapp/todolist/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class TodolistConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'todolist' diff --git a/webapps/django/todoapp/todolist/models.py b/webapps/django/todoapp/todolist/models.py new file mode 100755 index 0000000..d3544cb --- /dev/null +++ b/webapps/django/todoapp/todolist/models.py @@ -0,0 +1,9 @@ +from django.db import models + +# Create your models here. +class Todo(models.Model): + title=models.CharField(max_length=350) + complete=models.BooleanField(default=False) + + def __str__(self): + return self.title diff --git a/webapps/django/todoapp/todolist/tests.py b/webapps/django/todoapp/todolist/tests.py new file mode 100755 index 0000000..7ce503c --- /dev/null +++ b/webapps/django/todoapp/todolist/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/webapps/django/todoapp/todolist/urls.py b/webapps/django/todoapp/todolist/urls.py new file mode 100755 index 0000000..d3b3f8f --- /dev/null +++ b/webapps/django/todoapp/todolist/urls.py @@ -0,0 +1,10 @@ +from django.urls import path + +from . import views + +urlpatterns = [ + path('', views.index, name="index"), + path('add', views.add, name="add"), + path('delete/', views.delete, name="delete"), + path('update/', views.update, name="update"), +] \ No newline at end of file diff --git a/webapps/django/todoapp/todolist/views.py b/webapps/django/todoapp/todolist/views.py new file mode 100755 index 0000000..0afac7a --- /dev/null +++ b/webapps/django/todoapp/todolist/views.py @@ -0,0 +1,33 @@ +from django.shortcuts import render, redirect +from django.views.decorators.http import require_http_methods + +from .models import Todo + +# Create your views here. +def index(request): + todos = Todo.objects.all() + return render(request, "base.html", {"todo_list": todos}) + # return HttpResponse("Hello World!!") + + +@require_http_methods(["POST"]) +def add(request): + # if request.method == "POST": + title = request.POST["title"] + todo = Todo(title=title) + todo.save() + return redirect("index") + + +def update(request, todo_id): + todo = Todo.objects.get(id=todo_id) + todo.complete = not todo.complete + todo.save() + return redirect("index") + + +def delete(request, todo_id): + todo = Todo.objects.get(id=todo_id) + todo.delete() + return redirect("index") + diff --git a/webapps/fastapi/README.md b/webapps/fastapi/README.md new file mode 100755 index 0000000..247d71a --- /dev/null +++ b/webapps/fastapi/README.md @@ -0,0 +1,7 @@ +```console +pip install fastapi +pip install "uvicorn[standard]" +pip install python-multipart sqlalchemy jinja2 + +uvicorn app:app --reload +``` diff --git a/webapps/fastapi/app.py b/webapps/fastapi/app.py new file mode 100755 index 0000000..c5e6ba9 --- /dev/null +++ b/webapps/fastapi/app.py @@ -0,0 +1,60 @@ +from fastapi import FastAPI, Depends, Request, Form, status + +from starlette.responses import RedirectResponse +from starlette.templating import Jinja2Templates + +from sqlalchemy.orm import Session + +import models +from database import SessionLocal, engine + +models.Base.metadata.create_all(bind=engine) + +templates = Jinja2Templates(directory="templates") + +app = FastAPI() + + +# Dependency +def get_db(): + db = SessionLocal() + try: + yield db + finally: + db.close() + + +@app.get("/") +def home(request: Request, db: Session = Depends(get_db)): + todos = db.query(models.Todo).all() + return templates.TemplateResponse("base.html", + {"request": request, "todo_list": todos}) + +@app.post("/add") +def add(request: Request, title: str = Form(...), db: Session = Depends(get_db)): + new_todo = models.Todo(title=title) + db.add(new_todo) + db.commit() + + url = app.url_path_for("home") + return RedirectResponse(url=url, status_code=status.HTTP_303_SEE_OTHER) + + +@app.get("/update/{todo_id}") +def update(request: Request, todo_id: int, db: Session = Depends(get_db)): + todo = db.query(models.Todo).filter(models.Todo.id == todo_id).first() + todo.complete = not todo.complete + db.commit() + + url = app.url_path_for("home") + return RedirectResponse(url=url, status_code=status.HTTP_302_FOUND) + + +@app.get("/delete/{todo_id}") +def delete(request: Request, todo_id: int, db: Session = Depends(get_db)): + todo = db.query(models.Todo).filter(models.Todo.id == todo_id).first() + db.delete(todo) + db.commit() + + url = app.url_path_for("home") + return RedirectResponse(url=url, status_code=status.HTTP_302_FOUND) diff --git a/webapps/fastapi/database.py b/webapps/fastapi/database.py new file mode 100755 index 0000000..c888b88 --- /dev/null +++ b/webapps/fastapi/database.py @@ -0,0 +1,13 @@ +from sqlalchemy import create_engine +from sqlalchemy.ext.declarative import declarative_base +from sqlalchemy.orm import sessionmaker + +SQLALCHEMY_DATABASE_URL = "sqlite:///./db.sqlite" +# SQLALCHEMY_DATABASE_URL = "postgresql://user:password@postgresserver/db" + +engine = create_engine( + SQLALCHEMY_DATABASE_URL, connect_args={"check_same_thread": False} +) +SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) + +Base = declarative_base() \ No newline at end of file diff --git a/webapps/fastapi/models.py b/webapps/fastapi/models.py new file mode 100755 index 0000000..c109102 --- /dev/null +++ b/webapps/fastapi/models.py @@ -0,0 +1,14 @@ +from sqlalchemy import Boolean, Column, Integer, String + +from database import Base + + +class Todo(Base): + __tablename__ = "todos" + + id = Column(Integer, primary_key=True, index=True) + title = Column(String) + complete = Column(Boolean, default=False) + + +# schemas? \ No newline at end of file diff --git a/webapps/fastapi/templates/base.html b/webapps/fastapi/templates/base.html new file mode 100755 index 0000000..b40815c --- /dev/null +++ b/webapps/fastapi/templates/base.html @@ -0,0 +1,44 @@ + + + + + + + Todo App + + + + + + +
+

To Do App

+ +
+
+ +
+
+ +
+ +
+ + {% for todo in todo_list %} +
+

{{todo.id }} | {{ todo.title }}

+ + {% if todo.complete == False %} + Not Complete + {% else %} + Completed + {% endif %} + + Update + Delete +
+ {% endfor %} +
+ + + \ No newline at end of file diff --git a/webapps/flask/README.md b/webapps/flask/README.md new file mode 100644 index 0000000..34c35ff --- /dev/null +++ b/webapps/flask/README.md @@ -0,0 +1,11 @@ +```console +python3 -m venv venv +. venv/bin/activate + +pip install Flask +pip install Flask-SQLAlchemy + +export FLASK_APP=app.py +export FLASK_ENV=development +flask run +``` diff --git a/webapps/flask/app.py b/webapps/flask/app.py new file mode 100755 index 0000000..0bbf5ff --- /dev/null +++ b/webapps/flask/app.py @@ -0,0 +1,54 @@ +from flask import Flask, render_template, request, redirect, url_for +from flask_sqlalchemy import SQLAlchemy + +app = Flask(__name__) + +# /// = relative path, //// = absolute path +app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///db.sqlite' +app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False +db = SQLAlchemy(app) + + +class Todo(db.Model): + id = db.Column(db.Integer, primary_key=True) + title = db.Column(db.String(100)) + complete = db.Column(db.Boolean) + + +db.create_all() + + +@app.get("/") +def home(): + # todo_list = Todo.query.all() + todo_list = db.session.query(Todo).all() + # return "Hello, World!" + return render_template("base.html", todo_list=todo_list) + + +# @app.route("/add", methods=["POST"]) +@app.post("/add") +def add(): + title = request.form.get("title") + new_todo = Todo(title=title, complete=False) + db.session.add(new_todo) + db.session.commit() + return redirect(url_for("home")) + + +@app.get("/update/") +def update(todo_id): + # todo = Todo.query.filter_by(id=todo_id).first() + todo = db.session.query(Todo).filter(Todo.id == todo_id).first() + todo.complete = not todo.complete + db.session.commit() + return redirect(url_for("home")) + + +@app.get("/delete/") +def delete(todo_id): + # todo = Todo.query.filter_by(id=todo_id).first() + todo = db.session.query(Todo).filter(Todo.id == todo_id).first() + db.session.delete(todo) + db.session.commit() + return redirect(url_for("home")) diff --git a/webapps/flask/templates/base.html b/webapps/flask/templates/base.html new file mode 100755 index 0000000..b40815c --- /dev/null +++ b/webapps/flask/templates/base.html @@ -0,0 +1,44 @@ + + + + + + + Todo App + + + + + + +
+

To Do App

+ +
+
+ +
+
+ +
+ +
+ + {% for todo in todo_list %} +
+

{{todo.id }} | {{ todo.title }}

+ + {% if todo.complete == False %} + Not Complete + {% else %} + Completed + {% endif %} + + Update + Delete +
+ {% endfor %} +
+ + + \ No newline at end of file