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/apple-health-analytics/README.md b/apple-health-analytics/README.md new file mode 100644 index 0000000..c3dd95c --- /dev/null +++ b/apple-health-analytics/README.md @@ -0,0 +1,8 @@ +## Analyze Apple Health data + +Dependencies: numpy, pandas, matplotlib + +## Export health data + +On iPhone go to Health App -> Profile -> Export data -> Send to your computer + diff --git a/apple-health-analytics/applehealth.ipynb b/apple-health-analytics/applehealth.ipynb new file mode 100644 index 0000000..8c9826a --- /dev/null +++ b/apple-health-analytics/applehealth.ipynb @@ -0,0 +1,938 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 63, + "id": "b8d3b21b", + "metadata": {}, + "outputs": [], + "source": [ + "import xml.etree.ElementTree as ET\n", + "#from lxml import etree\n", + "import pandas as pd\n", + "import time\n", + "import numpy as np\n", + "import datetime as dt\n", + "# dt.datetime.strptime" + ] + }, + { + "cell_type": "code", + "execution_count": 64, + "id": "87e15e18", + "metadata": {}, + "outputs": [], + "source": [ + "# create element tree object\n", + "\n", + "tree = ET.parse('data/export.xml') \n", + "# for every health record, extract the attributes into a dictionary (columns). Then create a list (rows)\n", + "root = tree.getroot()\n", + "record_list = [x.attrib for x in root.iter('Record')]\n", + "\n", + "# create DataFrame from a list (rows) of dictionaries (columns)\n", + "record_data = pd.DataFrame(record_list)\n", + "\n", + "# proper type to dates\n", + "for col in ['creationDate', 'startDate', 'endDate']:\n", + " record_data[col] = pd.to_datetime(record_data[col])\n", + "\n", + "# value is numeric, NaN if fails\n", + "record_data['value'] = pd.to_numeric(record_data['value'], errors='coerce')\n", + "\n", + "# some records do not measure anything, just count occurences\n", + "# filling with 1.0 (= one time) makes it easier to aggregate\n", + "record_data['value'] = record_data['value'].fillna(1.0)\n", + "\n", + "# shorter observation names: use vectorized replace function\n", + "record_data['type'] = record_data['type'].str.replace('HKQuantityTypeIdentifier', '')\n", + "record_data['type'] = record_data['type'].str.replace('HKCategoryTypeIdentifier', '')" + ] + }, + { + "cell_type": "code", + "execution_count": 65, + "id": "5d40e382", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
typesourceNamesourceVersionunitcreationDatestartDateendDatevaluedevice
743765HeartRateVariabilitySDNNApple Watch von Patrick7.6.1ms2021-08-20 21:44:34+02:002021-08-20 21:43:28+02:002021-08-20 21:44:34+02:00106.2270<<HKDevice: 0x2811a01e0>, name:Apple Watch, ma...
743766HeartRateVariabilitySDNNApple Watch von Patrick7.6.1ms2021-08-20 21:48:35+02:002021-08-20 21:47:30+02:002021-08-20 21:48:35+02:0060.9539<<HKDevice: 0x2811a01e0>, name:Apple Watch, ma...
743767HeartRateVariabilitySDNNApple Watch von Patrick7.6.1ms2021-08-22 08:19:29+02:002021-08-22 08:18:23+02:002021-08-22 08:19:29+02:0071.2487<<HKDevice: 0x2811a01e0>, name:Apple Watch, ma...
743768HeartRateVariabilitySDNNApple Watch von Patrick7.6.1ms2021-08-22 09:52:59+02:002021-08-22 09:51:54+02:002021-08-22 09:52:59+02:0087.2668<<HKDevice: 0x2811a01e0>, name:Apple Watch, ma...
743769HeartRateVariabilitySDNNApple Watch von Patrick7.6.1ms2021-08-22 14:00:49+02:002021-08-22 13:59:45+02:002021-08-22 14:00:49+02:0042.2700<<HKDevice: 0x2811a01e0>, name:Apple Watch, ma...
\n", + "
" + ], + "text/plain": [ + " type sourceName sourceVersion unit \\\n", + "743765 HeartRateVariabilitySDNN Apple Watch von Patrick 7.6.1 ms \n", + "743766 HeartRateVariabilitySDNN Apple Watch von Patrick 7.6.1 ms \n", + "743767 HeartRateVariabilitySDNN Apple Watch von Patrick 7.6.1 ms \n", + "743768 HeartRateVariabilitySDNN Apple Watch von Patrick 7.6.1 ms \n", + "743769 HeartRateVariabilitySDNN Apple Watch von Patrick 7.6.1 ms \n", + "\n", + " creationDate startDate \\\n", + "743765 2021-08-20 21:44:34+02:00 2021-08-20 21:43:28+02:00 \n", + "743766 2021-08-20 21:48:35+02:00 2021-08-20 21:47:30+02:00 \n", + "743767 2021-08-22 08:19:29+02:00 2021-08-22 08:18:23+02:00 \n", + "743768 2021-08-22 09:52:59+02:00 2021-08-22 09:51:54+02:00 \n", + "743769 2021-08-22 14:00:49+02:00 2021-08-22 13:59:45+02:00 \n", + "\n", + " endDate value \\\n", + "743765 2021-08-20 21:44:34+02:00 106.2270 \n", + "743766 2021-08-20 21:48:35+02:00 60.9539 \n", + "743767 2021-08-22 08:19:29+02:00 71.2487 \n", + "743768 2021-08-22 09:52:59+02:00 87.2668 \n", + "743769 2021-08-22 14:00:49+02:00 42.2700 \n", + "\n", + " device \n", + "743765 <, name:Apple Watch, ma... \n", + "743766 <, name:Apple Watch, ma... \n", + "743767 <, name:Apple Watch, ma... \n", + "743768 <, name:Apple Watch, ma... \n", + "743769 <, name:Apple Watch, ma... " + ] + }, + "execution_count": 65, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "record_data.tail()" + ] + }, + { + "cell_type": "code", + "execution_count": 66, + "id": "b25f460b", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "RangeIndex: 743770 entries, 0 to 743769\n", + "Data columns (total 9 columns):\n", + " # Column Non-Null Count Dtype \n", + "--- ------ -------------- ----- \n", + " 0 type 743770 non-null object \n", + " 1 sourceName 743770 non-null object \n", + " 2 sourceVersion 738284 non-null object \n", + " 3 unit 739683 non-null object \n", + " 4 creationDate 743770 non-null datetime64[ns, pytz.FixedOffset(120)]\n", + " 5 startDate 743770 non-null datetime64[ns, pytz.FixedOffset(120)]\n", + " 6 endDate 743770 non-null datetime64[ns, pytz.FixedOffset(120)]\n", + " 7 value 743770 non-null float64 \n", + " 8 device 697925 non-null object \n", + "dtypes: datetime64[ns, pytz.FixedOffset(120)](3), float64(1), object(5)\n", + "memory usage: 51.1+ MB\n" + ] + } + ], + "source": [ + "record_data.info()" + ] + }, + { + "cell_type": "code", + "execution_count": 67, + "id": "8b0198ea", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
TypedurationdurationUnittotalDistancetotalDistanceUnittotalEnergyBurnedtotalEnergyBurnedUnitsourceNamesourceVersiondevicecreationDatestartDateendDate
180Running18.876555min3.026288km202.358105kcalApple Watch von Patrick7.2<<HKDevice: 0x2811cf160>, name:Apple Watch, ma...2021-08-18 08:08:20+02:002021-08-18 07:49:27+02:002021-08-18 08:08:19+02:00
181Running41.686440min7.154993km481.677116kcalApple Watch von Patrick7.2<<HKDevice: 0x2811cf160>, name:Apple Watch, ma...2021-08-19 09:49:37+02:002021-08-19 09:07:54+02:002021-08-19 09:49:36+02:00
182Running19.186911min3.028681km201.441305kcalApple Watch von Patrick7.6.1<<HKDevice: 0x2811da490>, name:Apple Watch, ma...2021-08-20 19:03:11+02:002021-08-20 18:43:58+02:002021-08-20 19:03:09+02:00
183Running20.134952min3.035656km199.521000kcalApple Watch von Patrick7.6.1<<HKDevice: 0x2811da490>, name:Apple Watch, ma...2021-08-21 17:29:21+02:002021-08-21 17:09:11+02:002021-08-21 17:29:19+02:00
184Yoga22.080947min0.000000km50.902514kcalApple Watch von Patrick7.6.1<<HKDevice: 0x2811da490>, name:Apple Watch, ma...2021-08-22 08:47:27+02:002021-08-22 08:25:21+02:002021-08-22 08:47:26+02:00
\n", + "
" + ], + "text/plain": [ + " Type duration durationUnit totalDistance totalDistanceUnit \\\n", + "180 Running 18.876555 min 3.026288 km \n", + "181 Running 41.686440 min 7.154993 km \n", + "182 Running 19.186911 min 3.028681 km \n", + "183 Running 20.134952 min 3.035656 km \n", + "184 Yoga 22.080947 min 0.000000 km \n", + "\n", + " totalEnergyBurned totalEnergyBurnedUnit sourceName \\\n", + "180 202.358105 kcal Apple Watch von Patrick \n", + "181 481.677116 kcal Apple Watch von Patrick \n", + "182 201.441305 kcal Apple Watch von Patrick \n", + "183 199.521000 kcal Apple Watch von Patrick \n", + "184 50.902514 kcal Apple Watch von Patrick \n", + "\n", + " sourceVersion device \\\n", + "180 7.2 <, name:Apple Watch, ma... \n", + "181 7.2 <, name:Apple Watch, ma... \n", + "182 7.6.1 <, name:Apple Watch, ma... \n", + "183 7.6.1 <, name:Apple Watch, ma... \n", + "184 7.6.1 <, name:Apple Watch, ma... \n", + "\n", + " creationDate startDate \\\n", + "180 2021-08-18 08:08:20+02:00 2021-08-18 07:49:27+02:00 \n", + "181 2021-08-19 09:49:37+02:00 2021-08-19 09:07:54+02:00 \n", + "182 2021-08-20 19:03:11+02:00 2021-08-20 18:43:58+02:00 \n", + "183 2021-08-21 17:29:21+02:00 2021-08-21 17:09:11+02:00 \n", + "184 2021-08-22 08:47:27+02:00 2021-08-22 08:25:21+02:00 \n", + "\n", + " endDate \n", + "180 2021-08-18 08:08:19+02:00 \n", + "181 2021-08-19 09:49:36+02:00 \n", + "182 2021-08-20 19:03:09+02:00 \n", + "183 2021-08-21 17:29:19+02:00 \n", + "184 2021-08-22 08:47:26+02:00 " + ] + }, + "execution_count": 67, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "workout_list = [x.attrib for x in root.iter('Workout')]\n", + "\n", + "# create DataFrame from a list (rows) of dictionaries (columns)\n", + "workout_data = pd.DataFrame(workout_list)\n", + "workout_data['workoutActivityType'] = workout_data['workoutActivityType'].str.replace('HKWorkoutActivityType', '')\n", + "workout_data = workout_data.rename({\"workoutActivityType\": \"Type\"}, axis=1)\n", + "# proper type to dates\n", + "for col in ['creationDate', 'startDate', 'endDate']:\n", + " workout_data[col] = pd.to_datetime(workout_data[col])\n", + " \n", + "workout_data['duration'] = pd.to_numeric(workout_data['duration'])\n", + "workout_data['totalEnergyBurned'] = pd.to_numeric(workout_data['totalEnergyBurned'])\n", + "workout_data['totalDistance'] = pd.to_numeric(workout_data['totalDistance'])\n", + "workout_data.tail()" + ] + }, + { + "cell_type": "code", + "execution_count": 68, + "id": "a9c05a3f", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
TypedurationdurationUnittotalDistancetotalDistanceUnittotalEnergyBurnedtotalEnergyBurnedUnitsourceNamesourceVersiondevicecreationDatestartDateendDate
183Running20.134952min3.035656km199.521kcalApple Watch von Patrick7.6.1<<HKDevice: 0x2811da490>, name:Apple Watch, ma...2021-08-21 17:29:21+02:002021-08-21 17:09:11+02:002021-08-21 17:29:19+02:00
\n", + "
" + ], + "text/plain": [ + " Type duration durationUnit totalDistance totalDistanceUnit \\\n", + "183 Running 20.134952 min 3.035656 km \n", + "\n", + " totalEnergyBurned totalEnergyBurnedUnit sourceName \\\n", + "183 199.521 kcal Apple Watch von Patrick \n", + "\n", + " sourceVersion device \\\n", + "183 7.6.1 <, name:Apple Watch, ma... \n", + "\n", + " creationDate startDate \\\n", + "183 2021-08-21 17:29:21+02:00 2021-08-21 17:09:11+02:00 \n", + "\n", + " endDate \n", + "183 2021-08-21 17:29:19+02:00 " + ] + }, + "execution_count": 68, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "last_run = workout_data.iloc[[-2]]\n", + "last_run" + ] + }, + { + "cell_type": "code", + "execution_count": 69, + "id": "29bfeadd", + "metadata": {}, + "outputs": [], + "source": [ + "def get_heartrate_for_date(start, end, heartrate):\n", + " heartrate = heartrate[heartrate[\"startDate\"] >= start]\n", + " heartrate = heartrate[heartrate[\"endDate\"] <= end]\n", + " return heartrate\n", + "\n", + "def get_heartrate_for_workout(workout, heartrate):\n", + " return get_heartrate_for_date(workout[\"startDate\"].item(), workout[\"endDate\"].item(), heartrate)" + ] + }, + { + "cell_type": "code", + "execution_count": 70, + "id": "fa4b7796", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(73.0, 136.0, 125.71784232365145)" + ] + }, + "execution_count": 70, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "heartrate = record_data[record_data[\"type\"] == \"HeartRate\"]\n", + "\n", + "heartrate = get_heartrate_for_workout(last_run, heartrate)\n", + "minh = heartrate[\"value\"].min()\n", + "maxh = heartrate[\"value\"].max()\n", + "meanh = heartrate[\"value\"].mean()\n", + "minh, maxh, meanh" + ] + }, + { + "cell_type": "code", + "execution_count": 71, + "id": "68be0af3", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 71, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "heartrate.plot(x='endDate', y='value', style='r|', markersize=8.5, figsize=(12, 6))" + ] + }, + { + "cell_type": "code", + "execution_count": 72, + "id": "a15cb5d6", + "metadata": {}, + "outputs": [], + "source": [ + "today = dt.date.today()\n", + "\n", + "xdaysago = today - dt.timedelta(days=7)\n", + "first_of_month = today - dt.timedelta(days=today.day - 1)" + ] + }, + { + "cell_type": "code", + "execution_count": 73, + "id": "868b3b70", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "datetime.date(2021, 8, 22)" + ] + }, + "execution_count": 73, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "today" + ] + }, + { + "cell_type": "code", + "execution_count": 74, + "id": "0718242f", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "datetime.date(2021, 8, 15)" + ] + }, + "execution_count": 74, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "xdaysago" + ] + }, + { + "cell_type": "code", + "execution_count": 75, + "id": "ca6b2329", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "datetime.date(2021, 8, 1)" + ] + }, + "execution_count": 75, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "first_of_month" + ] + }, + { + "cell_type": "code", + "execution_count": 76, + "id": "d4f6ffad", + "metadata": {}, + "outputs": [], + "source": [ + "xdaysago = today - dt.timedelta(days=33)\n", + "\n", + "time_to_check = pd.to_datetime(first_of_month, utc=True)\n", + "time_to_check = pd.to_datetime(xdaysago, utc=True)\n", + "runs_last_month = workout_data[workout_data[\"creationDate\"] >= time_to_check]\n", + "runs_last_month = runs_last_month[runs_last_month[\"Type\"] == \"Running\"]\n", + "runs_last_month = runs_last_month.drop(columns=[\"device\", \"sourceVersion\"])" + ] + }, + { + "cell_type": "code", + "execution_count": 77, + "id": "8c7310fc", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(755.4174678126972, 129.0223653750448, 8521.206610642746)" + ] + }, + "execution_count": 77, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "duration_sum = runs_last_month[\"duration\"].sum()\n", + "distance_sum = runs_last_month[\"totalDistance\"].sum()\n", + "energy_sum = runs_last_month[\"totalEnergyBurned\"].sum()\n", + "duration_sum, distance_sum, energy_sum" + ] + }, + { + "cell_type": "code", + "execution_count": 78, + "id": "f5e4c561", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(22.89143841856658, 3.909768647728631, 258.2183821406893)" + ] + }, + "execution_count": 78, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "duration_avg = runs_last_month[\"duration\"].mean()\n", + "distance_avg = runs_last_month[\"totalDistance\"].mean()\n", + "energy_avg = runs_last_month[\"totalEnergyBurned\"].mean()\n", + "duration_avg, distance_avg, energy_avg" + ] + }, + { + "cell_type": "code", + "execution_count": 79, + "id": "330f1284", + "metadata": {}, + "outputs": [], + "source": [ + "def get_heartrate_for_workout(workout, heartrate):\n", + " return get_heartrate_for_date(workout[\"startDate\"], workout[\"endDate\"], heartrate)\n", + "\n", + "def convert_to_minute_proportion(number):\n", + " return int(number) + ((number % 1) / 100 * 60)\n", + "heartrate = record_data[record_data[\"type\"] == \"HeartRate\"]\n", + "runs_last_month[\"heartrate\"] = runs_last_month.apply(lambda row: get_heartrate_for_workout(row, heartrate), axis=1)\n", + "runs_last_month[\"hr_mean\"] = runs_last_month.apply(lambda row: row['heartrate'][\"value\"].mean(), axis=1)\n", + "pace = runs_last_month[\"duration\"] / runs_last_month[\"totalDistance\"]\n", + "# convert decimals to minute percentage, pace=min/km\n", + "pace = pace.apply(lambda row: convert_to_minute_proportion(row))\n", + "runs_last_month[\"pace\"] = pace" + ] + }, + { + "cell_type": "code", + "execution_count": 80, + "id": "3d4a4300", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
durationtotalDistancetotalEnergyBurnedcreationDate
15024.0170834.021084263.4237712021-07-20 17:48:58+02:00
15123.3044604.025121261.6740002021-07-21 17:35:13+02:00
15222.9190204.027181268.2200082021-07-22 17:04:27+02:00
15323.0905794.281389276.9799342021-07-23 17:49:31+02:00
15424.0157624.119525269.0362182021-07-24 17:57:31+02:00
\n", + "
" + ], + "text/plain": [ + " duration totalDistance totalEnergyBurned creationDate\n", + "150 24.017083 4.021084 263.423771 2021-07-20 17:48:58+02:00\n", + "151 23.304460 4.025121 261.674000 2021-07-21 17:35:13+02:00\n", + "152 22.919020 4.027181 268.220008 2021-07-22 17:04:27+02:00\n", + "153 23.090579 4.281389 276.979934 2021-07-23 17:49:31+02:00\n", + "154 24.015762 4.119525 269.036218 2021-07-24 17:57:31+02:00" + ] + }, + "execution_count": 80, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "import matplotlib.pyplot as plt\n", + "\n", + "plot_data = runs_last_month[['duration', 'totalDistance', 'totalEnergyBurned', 'creationDate']].copy()\n", + "plot_data.head()" + ] + }, + { + "cell_type": "code", + "execution_count": 81, + "id": "15860703", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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6Hqcikqp7CAiIkoFZ7CIiCjQZFVKbYKUA8ZCmlp87LpuLIBflFKPAFgM4LLr8i0AYpVSmwFMBvAZpLPgRqWUAnAKQEdrwiciIn/GJhdEREREREQGYYkgERERERGRQZhgERERERERGYQJFhERERERkUGYYBERERERERmECRYREREREZFBmGAREREREREZhAkWERERERGRQZhgERERERERGYQJFhERERERkUGYYBERERERERmECRYREREREZFBQu0OID0KFCigS5cubXcYXrl8+TKyZ89udxhBgWNtHY51QhwPa3G8rcOxtg7H2joca2sF4nhv2LDhtNa6YOLL/TrBKl26NNavX293GF5ZsmQJwsPD7Q4jKHCsrcOxTojjYS2Ot3U41tbhWFuHY22tQBxvpdS/SV3OEkEiIiIiIiKDMMEiIiIiIiIyCBMsIiIiIiIigzDBIiIiIiIiMggTLCIioiBx8CCweDGgtd2REBEFLiZYREREQeD0aSA8HGjZEujQAfg3yd5XRESUXkywiIiIAlxcHNClC3D8OPDKK0BkJFC1KjByJHDzpt3RERElLzYW+OQT4Omn7Y7Ee0ywiIiIAtybbwIREcDYscCoUcA//wCtWgGvvQbUqQNERdkdIRHR7aKigLp1gQEDgBMngGvX7I7IO0ywiIiIAtivvwIffAD07Ak884xcVrIkMHu2fF24ADRtCjz7LHD2rJ2REhGJU6dkxqppU/lc+vVX4PffgSxZ7I7MO0ywiIiIAtTOnUCPHkCDBsDnn99+fYcOwI4dUjb47bdA5crA99+zCQYR2SM+HvjqK6BSJfksevVVmXHv1AlQyu7ovMcEi4iIKABdvCgHJVmzAr/8AmTOnPTtcuSQssENG4By5YDu3aURxs6d1sZLRMFt40agcWPgueeAmjWBTZuADz+Uzyh/wwSLiIgowGgNPPkksGcP8NNPQPHiqd/njjtkvcP48XJgU7Mm8NZbwNWrZkdLRMHs/Hmgb1+gfn3gwAGZuVq8GKhWze7IfMcEi4iIKMB8+CEwa5Z0CQwP9/5+ISFy9njnTuDRR4Hhw4EaNYB16/KaFisRBSetgWnTpDR57FjghReAXbuAbt38qxwwKUywiIiIAsjChcAbbwCPPQa89JJvjxEWBkydKp0HQ0KAV1+9478270RE6bVjh5Qid+sGlCoFrF0LfPEFkCeP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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "# plot hr, pace, distance\n", + "fig, axs = plt.subplots(3, 1, figsize=(12, 8))\n", + "\n", + "x = runs_last_month['creationDate']\n", + "axs[0].plot(x, runs_last_month[\"hr_mean\"], color=\"red\")\n", + "axs[0].set_xlabel('Date')\n", + "axs[0].set_ylabel('HR mean')\n", + "axs[0].grid(True)\n", + "\n", + "axs[1].plot(x, runs_last_month[\"pace\"], color=\"blue\")\n", + "axs[1].set_ylabel('Pace')\n", + "axs[1].grid(True)\n", + "\n", + "axs[2].plot(x, runs_last_month[\"totalDistance\"], color=\"green\")\n", + "axs[2].set_ylabel('Distance')\n", + "axs[2].grid(True)\n", + "\n", + "fig.tight_layout()\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": 82, + "id": "35e3cf85", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(129.0223653750448,\n", + " 12.354174678126972,\n", + " 8521.206610642746,\n", + " 143.81744261405984,\n", + " 5.534279971791522)" + ] + }, + "execution_count": 82, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Get stats\n", + "total_dist = runs_last_month[\"totalDistance\"].sum()\n", + "total_time = runs_last_month[\"duration\"].sum() / 60\n", + "total_time = convert_to_minute_proportion(total_time)\n", + "total_kcal = runs_last_month[\"totalEnergyBurned\"].sum()\n", + "hr_mean = runs_last_month[\"hr_mean\"].mean()\n", + "pace_mean = (runs_last_month[\"duration\"] / runs_last_month[\"totalDistance\"]).mean()\n", + "pace_mean = convert_to_minute_proportion(pace_mean)\n", + "total_dist, total_time, total_kcal, hr_mean, pace_mean" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "eeecc857", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "ml", + "language": "python", + "name": "ml" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.2" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} 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/notetaking-speech-rec/README.md b/notetaking-speech-rec/README.md new file mode 100644 index 0000000..817c76b --- /dev/null +++ b/notetaking-speech-rec/README.md @@ -0,0 +1,27 @@ +# Create a Notetaking App with Speech Recognition + +## Installation + +On Mac you also need: +``` +$ brew install portaudio +$ pip install pyobjc + +``` + +Then use: +``` +# pip install pyaudio +# pip install speechrecognition +# pip install requests gtts playsound +``` + +Note: On a M1 Mac you may need to use this command to install pyaudio +``` +# python -m pip install --global-option='build_ext' --global-option='-I/opt/homebrew/Cellar/portaudio/19.7.0/include' --global-option='-L/opt/homebrew/Cellar/portaudio/19.7.0/lib' pyaudio +``` + +For more setup instructions also have a look here: +- [Pyaudio Installation](http://people.csail.mit.edu/hubert/pyaudio/) +- [Speech Recognition](https://github.com/Uberi/speech_recognition) +- [Notion API setup](https://developers.notion.com/docs/getting-started) \ No newline at end of file diff --git a/notetaking-speech-rec/main.py b/notetaking-speech-rec/main.py new file mode 100644 index 0000000..9c0bbaa --- /dev/null +++ b/notetaking-speech-rec/main.py @@ -0,0 +1,65 @@ +import speech_recognition as sr +import gtts +from playsound import playsound +import os +from datetime import datetime +from notion import NotionClient + +r = sr.Recognizer() + +token = "YOUR NOTION TOKEN HERE" +database_id = "YOUR NOTION DATABASE_ID HERE" + +client = NotionClient(token, database_id) + +ACTIVATION_COMMAND = "hey sam" + +def get_audio(): + with sr.Microphone() as source: + print("Say something") + audio = r.listen(source) + return audio + +def audio_to_text(audio): + text = "" + try: + text = r.recognize_google(audio) + except sr.UnknownValueError: + print("Speech recognition could not understand audio") + except sr.RequestError: + print("could not request results from API") + return text + +def play_sound(text): + try: + tts = gtts.gTTS(text) + tempfile = "./temp.mp3" + tts.save(tempfile) + playsound(tempfile) + os.remove(tempfile) + except AssertionError: + print("could not play sound") + + + +if __name__ == "__main__": + + while True: + a = get_audio() + command = audio_to_text(a) + + if ACTIVATION_COMMAND in command.lower(): + print("activate") + play_sound("What can I do for you?") + + note = get_audio() + note = audio_to_text(note) + + if note: + play_sound(note) + + now = datetime.now().astimezone().isoformat() + res = client.create_page(note, now, status="Active") + if res.status_code == 200: + play_sound("Stored new item") + diff --git a/notetaking-speech-rec/notion.py b/notetaking-speech-rec/notion.py new file mode 100644 index 0000000..31b9031 --- /dev/null +++ b/notetaking-speech-rec/notion.py @@ -0,0 +1,52 @@ +import json +import requests + + +class NotionClient: + + def __init__(self, token, database_id) -> None: + self.database_id = database_id + + self.headers = { + "Authorization": "Bearer " + token, + "Content-Type": "application/json", + "Notion-Version": "2021-08-16" + } + + # read, update + def create_page(self, description, date, status): + create_url = 'https://api.notion.com/v1/pages' + + data = { + "parent": { "database_id": self.database_id }, + "properties": { + "Description": { + "title": [ + { + "text": { + "content": description + } + } + ] + }, + "Date": { + "date": { + "start": date, + "end": None + } + }, + "Status": { + "rich_text": [ + { + "text": { + "content": status + } + } + ] + } + }} + + data = json.dumps(data) + res = requests.post(create_url, headers=self.headers, data=data) + print(res.status_code) + return res 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