diff --git a/__pycache__/app.cpython-312.pyc b/__pycache__/app.cpython-312.pyc new file mode 100644 index 0000000..8adf129 Binary files /dev/null and b/__pycache__/app.cpython-312.pyc differ diff --git a/__pycache__/config.cpython-312.pyc b/__pycache__/config.cpython-312.pyc new file mode 100644 index 0000000..af8c565 Binary files /dev/null and b/__pycache__/config.cpython-312.pyc differ diff --git a/app.py b/app.py index 79e3aac..5f85019 100644 --- a/app.py +++ b/app.py @@ -1,141 +1,191 @@ -from flask import Flask, render_template, request, redirect, url_for -import csv import os +import json +import logging from datetime import datetime, timezone +from typing import List, Dict, Any, Optional +from flask import Flask, request, render_template, redirect, url_for +from config import DATA_FILE_PATH app = Flask(__name__) -def load_data(): - if os.path.exists('data/streaks.csv'): - with open('data/streaks.csv', mode='r') as f: - reader = csv.DictReader(f) - return sorted(list(reader), key=lambda x: datetime.strptime(x['Date'], '%Y-%m-%d'), reverse=True) +logging.basicConfig(level=logging.INFO) + +USERS_FILE_PATH = 'data/users.json' + +def load_users() -> List[str]: + try: + if os.path.exists(USERS_FILE_PATH): + with open(USERS_FILE_PATH, 'r') as f: + users = json.load(f) + return users + except Exception as e: + logging.error(f"Error loading users: {e}") return [] -def save_data(data): - with open('data/streaks.csv', mode='w', newline='') as f: - fieldnames = ['Date', 'Saketh', 'Saketh_Difficulty', 'Aditya', 'Aditya_Difficulty', 'Kushagra', 'Kushagra_Difficulty'] - writer = csv.DictWriter(f, fieldnames=fieldnames) - writer.writeheader() - writer.writerows(data) +def save_users(users: List[str]) -> None: + try: + with open(USERS_FILE_PATH, 'w') as f: + json.dump(users, f, indent=4) + except Exception as e: + logging.error(f"Error saving users: {e}") -def convert_to_leetcode_url(question_text): +def convert_to_leetcode_url(question_text: str) -> Optional[str]: if ". " in question_text: - question_title = question_text.split(". ", 1)[1].strip().lower().replace(" ", "-") + question_title = question_text.split(". ", 1)[1] + question_title = question_title.strip().lower().replace(" ", "-") return f"https://leetcode.com/problems/{question_title}" - return None + return None + +def load_data() -> List[Dict[str, Any]]: + try: + if os.path.exists(DATA_FILE_PATH): + with open(DATA_FILE_PATH, 'r') as f: + data = json.load(f) + data = sorted( + data, + key=lambda x: datetime.strptime(x['Date'], '%Y-%m-%d'), + reverse=True + ) + return data + except Exception as e: + logging.error(f"Error loading data: {e}") + return [] + +def save_data(data: List[Dict[str, Any]]) -> None: + try: + with open(DATA_FILE_PATH, 'w') as f: + json.dump(data, f, indent=4) + except Exception as e: + logging.error(f"Error saving data: {e}") -def add_default_entry_if_new_day(data): +def add_default_entry_if_new_day(data: List[Dict[str, Any]]) -> None: today = datetime.now(timezone.utc).date() today_str = today.strftime('%Y-%m-%d') existing_dates = {entry['Date'] for entry in data} - # If today is not in the existing dates, add a new entry if today_str not in existing_dates: + users = load_users() + user_data = { + user: { + 'Questions': [], + 'Difficulties': [] + } for user in users + } data.append({ 'Date': today_str, - 'Saketh': '', - 'Saketh_Difficulty': None, - 'Aditya': '', - 'Aditya_Difficulty': None, - 'Kushagra': '', - 'Kushagra_Difficulty': None + 'Users': user_data }) - save_data(data) # Save the new entry to the CSV file + save_data(data) @app.route('/') -def index(): +def index() -> str: data = load_data() - add_default_entry_if_new_day(data) # Check and add default entry for today + add_default_entry_if_new_day(data) + users = load_users() - # Generate links and structure question data for entry in data: - entry['Saketh_questions'] = [] - entry['Aditya_questions'] = [] - entry['Kushagra_questions'] = [] - - for q, d in zip(entry['Saketh'].split(','), entry['Saketh_Difficulty'].split(',')): - if entry['Saketh']: - question_url = convert_to_leetcode_url(q) - entry['Saketh_questions'].append((q.strip(), question_url, d.strip())) - - for q, d in zip(entry['Aditya'].split(','), entry['Aditya_Difficulty'].split(',')): - if entry['Aditya']: - question_url = convert_to_leetcode_url(q) - entry['Aditya_questions'].append((q.strip(), question_url, d.strip())) - - for q, d in zip(entry['Kushagra'].split(','), entry['Kushagra_Difficulty'].split(',')): - if entry['Kushagra']: + entry['User_questions'] = {} + for user in users: + entry['User_questions'][user] = [] + user_data = entry['Users'].get(user, {'Questions': [], 'Difficulties': []}) + questions = user_data['Questions'] + difficulties = user_data['Difficulties'] + for q, d in zip(questions, difficulties): question_url = convert_to_leetcode_url(q) - entry['Kushagra_questions'].append((q.strip(), question_url, d.strip())) + entry['User_questions'][user].append((q.strip(), question_url, d.strip())) - return render_template('index5.html', data=data) + return render_template('index.html', data=data, users=users) @app.route('/add', methods=['POST']) -def add(): +def add() -> Any: date = datetime.now().date().strftime('%Y-%m-%d') - saketh = request.form.get('saketh', '').strip() - saketh_difficulty = request.form.get('saketh_difficulty', None) - aditya = request.form.get('aditya', '').strip() - aditya_difficulty = request.form.get('aditya_difficulty', None) - kushagra = request.form.get('kushagra', '').strip() - kushagra_difficulty = request.form.get('kushagra_difficulty', None) - + users = load_users() data = load_data() entry_found = False for entry in data: if entry['Date'] == date: entry_found = True - if saketh: - entry['Saketh'] = f"{entry['Saketh']}, {saketh}" if entry['Saketh'] else saketh - entry['Saketh_Difficulty'] = f"{entry['Saketh_Difficulty']}, {saketh_difficulty}" if entry['Saketh_Difficulty'] and saketh_difficulty != None else saketh_difficulty - if aditya: - entry['Aditya'] = f"{entry['Aditya']}, {aditya}" if entry['Aditya'] else aditya - entry['Aditya_Difficulty'] = f"{entry['Aditya_Difficulty']}, {aditya_difficulty}" if entry['Aditya_Difficulty'] and aditya_difficulty != None else aditya_difficulty - if kushagra: - entry['Kushagra'] = f"{entry['Kushagra']}, {kushagra}" if entry['Kushagra'] else kushagra - entry['Kushagra_Difficulty'] = f"{entry['Kushagra_Difficulty']}, {kushagra_difficulty}" if entry['Kushagra_Difficulty'] and kushagra_difficulty != None else kushagra_difficulty + for user in users: + question = request.form.get(f'{user.lower()}', '').strip() + difficulty = request.form.get(f'{user.lower()}_difficulty', '') + if question: + user_data = entry['Users'].setdefault(user, {'Questions': [], 'Difficulties': []}) + user_data['Questions'].append(question) + user_data['Difficulties'].append(difficulty) break if not entry_found: + user_data = {} + for user in users: + question = request.form.get(f'{user.lower()}', '').strip() + difficulty = request.form.get(f'{user.lower()}_difficulty', '') + user_data[user] = { + 'Questions': [question] if question else [], + 'Difficulties': [difficulty] if difficulty else [] + } + data.append({ 'Date': date, - 'Saketh': saketh, - 'Saketh_Difficulty': saketh_difficulty, - 'Aditya': aditya, - 'Aditya_Difficulty': aditya_difficulty, - 'Kushagra': kushagra, - 'Kushagra_Difficulty': kushagra_difficulty + 'Users': user_data }) save_data(data) return redirect(url_for('index')) @app.route('/delete_question', methods=['POST']) -def delete_question(): +def delete_question() -> Any: date = request.form['date'] question = request.form['question'] - role = request.form['role'] + user = request.form['user'] data = load_data() - + for entry in data: if entry['Date'] == date: - # Remove the specific question from the appropriate list - questions = entry[role].split(',') - difficulties = entry[f'{role}_Difficulty'].split(',') - if question in questions: - index = questions.index(question) - questions.pop(index) - difficulties.pop(index) - - entry[role] = ', '.join(questions) - entry[f'{role}_Difficulty'] = ', '.join(difficulties) if difficulties else None + user_data = entry['Users'].get(user, {'Questions': [], 'Difficulties': []}) + if question in user_data['Questions']: + index = user_data['Questions'].index(question) + user_data['Questions'].pop(index) + user_data['Difficulties'].pop(index) break save_data(data) return redirect(url_for('index')) +@app.route('/add_user', methods=['POST']) +def add_user() -> Any: + new_user = request.form.get('new_user', '').strip() + if new_user: + users = load_users() + if new_user not in users: + users.append(new_user) + save_users(users) + + data = load_data() + for entry in data: + entry['Users'][new_user] = { + 'Questions': [], + 'Difficulties': [] + } + save_data(data) + return redirect(url_for('index')) + +@app.route('/delete_user', methods=['POST']) +def delete_user() -> Any: + user_to_delete = request.form.get('user_to_delete', '').strip() + if user_to_delete: + users = load_users() + if user_to_delete in users: + users.remove(user_to_delete) + save_users(users) + + data = load_data() + for entry in data: + if user_to_delete in entry['Users']: + del entry['Users'][user_to_delete] + save_data(data) + return redirect(url_for('index')) + if __name__ == '__main__': - app.run(debug=True) + app.run() \ No newline at end of file diff --git a/config.py b/config.py new file mode 100644 index 0000000..0e2d5ab --- /dev/null +++ b/config.py @@ -0,0 +1,3 @@ +import os + +DATA_FILE_PATH = os.getenv('DATA_FILE_PATH', 'data/streaks.json') \ No newline at end of file diff --git a/data/streaks.csv b/data/streaks.csv deleted file mode 100644 index 1dcdf63..0000000 --- a/data/streaks.csv +++ /dev/null @@ -1,18 +0,0 @@ -Date,Saketh,Saketh_Difficulty,Aditya,Aditya_Difficulty,Kushagra,Kushagra_Difficulty -2024-10-31,,,,,"283. Move Zeroes, 99. Recover Binary Search Tree","Easy, Medium" -2024-10-30,,,,,"26. Remove Duplicates from Sorted Array, 189. Rotate Array","Easy, Medium" -2024-10-29,,,83. Remove Duplicates from Sorted List,Easy,1305. All Elements In Two Binary Search Trees,Medium -2024-10-28,26. Remove Duplicates From Sorted Array,Easy,1863. Sum of All Subset XOR Totals,Easy,3211. Generate Binary Strings Without Adjacent Zeros,Medium -2024-10-27,1752. Check If Array Is Sorted And Rotated,Easy,540. Single Element in a Sorted Array,Medium,1689. Partitioning Into Minimum Number Of Deci Binary Numbers,Medium -2024-10-26,,,20. Valid Parentheses,Easy,1325. Delete Leaves With A Given Value,Medium -2024-10-25,,,235. Lowest Common Ancestor of a Binary Search Tree,Easy,1863. Sum Of All Subset XOR Totals,Easy -2024-10-24,,,231. Power of Two,Easy,136. Single Number,Easy -2024-10-23,,,235. Lowest Common Ancestor of a Binary Search Tree,Medium,231. Power Of Two,Easy -2024-10-22,,,"90. Subsets II,230. Kth Smallest Element in a BST","Medium, Medium","40. Combination Sum II,90. Subsets II","Medium, Medium" -2024-10-21,,,"450. Delete Node in a BST,701. Climbing Stairs","Medium, Medium","17. Letter Combinations of a Phone Number,701. Insert into a Binary Search Tree,216. Combination Sum III","Medium, Medium, Medium" -2024-10-20,,,,,"6. Zigzag Conversion,78. Subsets","Medium,Medium" -2024-10-19,,,,,"8. String to Integer (atoi),39. Combination Sum","Medium,Medium" -2024-10-18,,,,,1922. Count Good Numbers,Medium -2024-10-17,,,,,2433. Find The Original Array Of Prefix XOR,Medium -2024-10-16,,,203. Remove Linked List Elements,Medium,2326. Spiral Matrix IV,Medium -2024-10-15,,,153. Find Minimum in Rotated Sorted Array,Medium,1038. Binary Search Tree to Greater Sum Tree,Medium diff --git a/data/streaks.json b/data/streaks.json new file mode 100644 index 0000000..ab7bd78 --- /dev/null +++ b/data/streaks.json @@ -0,0 +1,19 @@ +[ + { + "Date": "2024-11-05", + "Users": { + "Aditya": { + "Questions": [], + "Difficulties": [] + }, + "Kushagra": { + "Questions": [], + "Difficulties": [] + }, + "Saketh": { + "Questions": [], + "Difficulties": [] + } + } + } +] \ No newline at end of file diff --git a/data/users.json b/data/users.json new file mode 100644 index 0000000..de85420 --- /dev/null +++ b/data/users.json @@ -0,0 +1,5 @@ +[ + "Aditya", + "Kushagra", + "Saketh" +] \ No newline at end of file diff --git a/static/css/styles.css b/static/css/styles.css index 0d6a6af..6c27188 100644 --- a/static/css/styles.css +++ b/static/css/styles.css @@ -18,13 +18,14 @@ h1 { .container { display: flex; max-width: 1200px; - margin: 0 auto; + margin: 20px auto; gap: 20px; align-items: flex-start; padding: 20px; background-color: #1f1f1f; border-radius: 12px; box-shadow: 0 8px 20px rgba(0, 0, 0, 0.5); + justify-content: space-around; } .table-section { @@ -115,9 +116,11 @@ option.hard { .form-container { background-color: #2b2b2b; padding: 20px; + margin: 20px auto; border: 1px solid #555555; border-radius: 12px; box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3); + justify-content: space-around; } input[type="text"], diff --git a/templates/index.html b/templates/index.html index 9faeb35..9c2394c 100644 --- a/templates/index.html +++ b/templates/index.html @@ -1,144 +1,116 @@ - + - - LCD - - - - -

- π™»πšŽπšŽπšπ™²πš˜πšπšŽπ™³πšŠπš’πš•πš’

+
- -
- - - - - - - - - - - {% for entry in data %} - - - - - - - {% endfor %} - -
DateSakethAdityaKushagra
{{ entry['Date'] }} - {% if entry['Saketh_questions']|length > 0 %} {% for question, - url, difficulty in entry['Saketh_questions'] %} - - {% endfor %} {% else %} missed :'( {% endif %} - - {% if entry['Aditya_questions']|length > 0 %} {% for question, - url, difficulty in entry['Aditya_questions'] %} - - {% endfor %} {% else %} missed :'( {% endif %} - - {% if entry['Kushagra_questions']|length > 0 %} {% for question, - url, difficulty in entry['Kushagra_questions'] %} - - {% endfor %} {% else %} missed :'( {% endif %} -
+ +
+

Add New User

+
+ + +
- +
-
-
- -
-
- - -
-
- - -
+

Delete User

+ + + + +
+
+ +
+ +
+
+ + + + + {% for user in users %} + + {% endfor %} + + + + {% for entry in data %} + + + {% for user in users %} + + {% endfor %} + + {% endfor %} + +
Date{{ user }}
{{ entry['Date'] }} + {% if entry['User_questions'][user]|length > 0 %} {% for q, + url, d in entry['User_questions'][user] %} + + {% endfor %} {% else %} missed ☹ {% endif %} +
+
+
+
+ +
+
+
+ {% for user in users %}
- - +
- -
- + {% endfor %} + + +
-