diff --git a/My-Personal-Journal/README.md b/My-Personal-Journal/README.md new file mode 100644 index 0000000..153fd47 --- /dev/null +++ b/My-Personal-Journal/README.md @@ -0,0 +1,34 @@ +# Personal Journal + +A simple web-based application for maintaining a personal journal. Users can create entries, tag them, and search through past entries by date or keywords. The application is built using Flask and SQLite for a lightweight and efficient experience. + +## Features + +- **Add New Entries**: Users can add journal entries with mood, content, and tags. +- **Search Entries**: Search through entries using keywords or specific dates. +- **Tag Management**: Create and view tags associated with each entry, and filter entries by tags. +- **User-Friendly Interface**: A clean and professional UI for easy navigation and use. + +## Technologies Used + +- Python +- Flask +- SQLite +- HTML/CSS + +## Installation + +1. **Clone the Repository**: + ```bash + git clone + cd your_project + +2. **Install Required Packages: Make sure you have Python installed (preferably Python 3). Install the required packages using pip**: + ```bash + pip install -r requirements.txt + +3. **Run the Application: Start the Flask application**: + ```bash + python app.py + +4. **Access the App: Open your web browser and navigate to http://127.0.0.1:8080 to access the application.** diff --git a/My-Personal-Journal/app.py b/My-Personal-Journal/app.py new file mode 100644 index 0000000..e5def9e --- /dev/null +++ b/My-Personal-Journal/app.py @@ -0,0 +1,78 @@ +from flask import Flask, render_template, request, redirect, url_for +from journal_app import add_entry, search_entries +from datetime import datetime +import sqlite3 + +app = Flask(__name__) + +# Route to show the home page +@app.route('/') +def index(): + return render_template('index.html') + +# Route to add a new journal entry +@app.route('/add', methods=['GET', 'POST']) +def add(): + if request.method == 'POST': + mood = request.form['mood'] + content = request.form['content'] + tags = request.form['tags'] + add_entry("user_1", mood, content, tags) + return redirect(url_for('index')) + return render_template('add.html') + +# Route to search journal entries + + +@app.route('/search', methods=['GET', 'POST']) +def search(): + if request.method == 'POST': + search_input = request.form['search_term'] + + # Try to parse the input as a date + try: + search_date = datetime.strptime(search_input, '%Y-%m-%d').date() + results = search_entries("user_1", date=search_date) + except ValueError: + # If parsing fails, treat it as a search term + results = search_entries("user_1", search_term=search_input) + + return render_template('search_results.html', results=results) + + return render_template('search.html') + + +# Route to list all unique tags +@app.route('/tags') +def list_tags(): + conn = sqlite3.connect('journal.db') + cursor = conn.cursor() + + # Fetch unique tags from all entries + cursor.execute("SELECT DISTINCT tags FROM journal_entries") + tags_data = cursor.fetchall() + conn.close() + + # Flatten the tags and remove duplicates + tags = set() + for row in tags_data: + if row[0]: + tags.update(tag.strip() for tag in row[0].split(',')) + + return render_template('tags.html', tags=sorted(tags)) + +# Route to show journal entries by tag +@app.route('/tags/') +def entries_by_tag(tag): + conn = sqlite3.connect('journal.db') + cursor = conn.cursor() + + # Search for entries that contain the selected tag + cursor.execute("SELECT * FROM journal_entries WHERE tags LIKE ?", ('%' + tag + '%',)) + results = cursor.fetchall() + conn.close() + + return render_template('search_results.html', results=results) + +if __name__ == '__main__': + app.run(debug=True, port=8080) diff --git a/My-Personal-Journal/database.py b/My-Personal-Journal/database.py new file mode 100644 index 0000000..ae7983b --- /dev/null +++ b/My-Personal-Journal/database.py @@ -0,0 +1,21 @@ +from sqlalchemy.orm import sessionmaker +from models import JournalEntry, Base +from sqlalchemy import create_engine + +engine = create_engine('sqlite:///journal.db') +Base.metadata.create_all(engine) +Session = sessionmaker(bind=engine) +session = Session() + +def add_entry(user_id, mood, content, tags): + entry = JournalEntry(user_id=user_id, mood=mood, content=content, tags=tags) + session.add(entry) + session.commit() + +def search_entries(user_id, search_term=None, date=None): + query = session.query(JournalEntry).filter(JournalEntry.user_id == user_id) + if search_term: + query = query.filter(JournalEntry.content.contains(search_term)) + if date: + query = query.filter(JournalEntry.date == date) + return query.all() diff --git a/My-Personal-Journal/journal_app.py b/My-Personal-Journal/journal_app.py new file mode 100644 index 0000000..3978159 --- /dev/null +++ b/My-Personal-Journal/journal_app.py @@ -0,0 +1,55 @@ +import sqlite3 +from datetime import datetime + +DB_NAME = "journal.db" + +def connect_db(): + return sqlite3.connect(DB_NAME) + +def create_table(): + with connect_db() as conn: + cursor = conn.cursor() + cursor.execute(''' + CREATE TABLE IF NOT EXISTS journal_entries ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + user_id TEXT, + date TEXT, + mood TEXT, + content TEXT, + tags TEXT + ) + ''') + conn.commit() + +def add_entry(user_id, mood, content, tags): + with connect_db() as conn: + cursor = conn.cursor() + cursor.execute(''' + INSERT INTO journal_entries (user_id, date, mood, content, tags) + VALUES (?, ?, ?, ?, ?) + ''', (user_id, datetime.now().strftime('%Y-%m-%d %H:%M:%S'), mood, content, tags)) + conn.commit() + +def search_entries(user_id, search_term=None, date=None): + with connect_db() as conn: + cursor = conn.cursor() + + if search_term: + query = ''' + SELECT * FROM journal_entries + WHERE user_id = ? AND (content LIKE ? OR tags LIKE ?) + ''' + cursor.execute(query, (user_id, f'%{search_term}%', f'%{search_term}%')) + elif date: + query = ''' + SELECT * FROM journal_entries + WHERE user_id = ? AND date(date) = ? + ''' + cursor.execute(query, (user_id, date.strftime('%Y-%m-%d'))) + else: + return [] + + return cursor.fetchall() + +# Create the journal table at the start +create_table() diff --git a/My-Personal-Journal/requirements.txt b/My-Personal-Journal/requirements.txt new file mode 100644 index 0000000..d41f170 --- /dev/null +++ b/My-Personal-Journal/requirements.txt @@ -0,0 +1,8 @@ +flask +sqlalchemy +# sqlite3 +bcrypt +pandas +matplotlib +# tkinter +flask \ No newline at end of file diff --git a/My-Personal-Journal/static/style.css b/My-Personal-Journal/static/style.css new file mode 100644 index 0000000..9c75f8d --- /dev/null +++ b/My-Personal-Journal/static/style.css @@ -0,0 +1,234 @@ +/* General Styles */ +body { + font-family: 'Arial', sans-serif; + margin: 0; + padding: 0; + background-color: #f4f4f9; + color: #333; +} + +h1, h2, h3, h4, h5, h6 { + color: #2c3e50; + text-align: center; +} + +/* Header */ +header { + background: #2980b9; + color: white; + padding: 20px 0; +} + +/* Main Content */ +.container { + max-width: 1100px; + margin: 20px auto; + padding: 20px; + background: white; + border-radius: 8px; + box-shadow: 0px 0px 15px rgba(0, 0, 0, 0.1); + text-align: center; /* Center text within the container */ +} + +/* Button Styles */ +.button-container { + display: flex; + justify-content: center; /* Center the buttons */ + flex-direction: column; /* Stack buttons vertically */ + gap: 20px; /* Space between buttons */ + margin-top: 30px; /* Add margin to the top */ +} + +.button { + display: inline-block; + padding: 15px 30px; + background-color: #2980b9; + color: white; + border-radius: 4px; + text-decoration: none; + font-size: 1.2rem; + transition: background-color 0.3s ease; +} + +.button:hover { + background-color: #3498db; +} + +/* Footer */ +footer { + text-align: center; + margin-top: 20px; + padding: 20px; + background-color: #2980b9; + color: white; +} + +p { + line-height: 1.6; + font-size: 1rem; +} + +a { + color: #3498db; + text-decoration: none; +} + +a:hover { + text-decoration: underline; +} + +/* Header */ +header { + background: #2980b9; + color: white; + padding: 20px 0; + text-align: center; +} + +/* Main Content */ +.container { + max-width: 1100px; + margin: 20px auto; + padding: 20px; + background: white; + border-radius: 8px; + box-shadow: 0px 0px 15px rgba(0, 0, 0, 0.1); +} + +h1 { + font-size: 2rem; + margin-bottom: 20px; +} + +/* Form Styles */ +.form-group { + margin-bottom: 20px; /* Space between form groups */ + text-align: left; /* Align labels and inputs to the left */ +} + +label { + display: block; /* Ensure labels are on separate lines */ + margin-bottom: 5px; /* Space between label and input */ +} + +/* Form Styles */ +form { + display: flex; + flex-direction: column; + gap: 10px; +} + +form input[type="text"], +form textarea { + padding: 10px; + font-size: 1rem; + border: 1px solid #ddd; + border-radius: 4px; +} + +form textarea { + resize: vertical; +} + +form input[type="submit"] { + background-color: #2980b9; + color: white; + border: none; + padding: 10px; + display:flex; + font-size: 1rem; + cursor: pointer; + border-radius: 4px; +} + +form input[type="submit"]:hover { + background-color: #3498db; +} + +/* Tag List */ +ul { + list-style: none; + padding: 0; + display: flex; + justify-content: center; /* Centers the tags horizontally */ + flex-wrap: wrap; /* Allows the tags to wrap if they exceed the width */ + gap: 10px; +} + +ul li { + display: inline-block; + margin-right: 10px; +} + +ul li a { + display: inline-block; + padding: 10px 20px; + background-color: #2980b9; + color: white; + border-radius: 4px; + transition: background-color 0.3s ease; +} + +ul li a:hover { + background-color: #3498db; +} + +/* Search Results */ +.search-results { + margin-top: 20px; +} + +.search-results p { + padding: 10px; + background: #ecf0f1; + border-left: 5px solid #2980b9; + margin-bottom: 10px; +} + +hr { + border: 0; + height: 1px; + background: #ddd; + margin: 20px 0; +} + +/* Footer */ +footer { + text-align: center; + margin-top: 20px; + padding: 20px; + background-color: #2980b9; + color: white; +} + +input[type="text"], +textarea, +select { + width: 100%; /* Take full width of the container */ + padding: 10px; /* Add padding for comfort */ + border: 1px solid #ccc; /* Border style */ + border-radius: 4px; /* Rounded corners */ + box-sizing: border-box; /* Include padding in width */ +} + +textarea { + height: 100px; /* Set a height for the textarea */ +} +/* Search Results Styles */ +.results { + margin-top: 30px; /* Space above search results */ + text-align: left; /* Align results to the left */ +} + +.results ul { + list-style-type: none; /* Remove default list styles */ + padding: 0; /* Remove padding */ +} + +.results li { + background-color: #f4f4f9; /* Light background for entries */ + border: 1px solid #ccc; /* Border for each entry */ + border-radius: 4px; /* Rounded corners */ + padding: 10px; /* Padding for comfort */ + margin-bottom: 15px; /* Space between entries */ +} \ No newline at end of file diff --git a/My-Personal-Journal/templates/add.html b/My-Personal-Journal/templates/add.html new file mode 100644 index 0000000..a7a7afe --- /dev/null +++ b/My-Personal-Journal/templates/add.html @@ -0,0 +1,45 @@ + + + + + + Add New Entry + + + +
+

Add New Journal Entry

+
+
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+ +
+
+
+

© 2024 Personal Journal

+
+ + diff --git a/My-Personal-Journal/templates/index.html b/My-Personal-Journal/templates/index.html new file mode 100644 index 0000000..99c420f --- /dev/null +++ b/My-Personal-Journal/templates/index.html @@ -0,0 +1,24 @@ + + + + + + Personal Journal + + + +
+

Welcome to Your Personal Journal

+
+ +
+

© 2024 Personal Journal

+
+ + diff --git a/My-Personal-Journal/templates/search.html b/My-Personal-Journal/templates/search.html new file mode 100644 index 0000000..850b1fa --- /dev/null +++ b/My-Personal-Journal/templates/search.html @@ -0,0 +1,16 @@ + + + + Search Journal Entries + + + + +
+

Search Journal Entries

+
+ Search by keyword or date (YYYY-MM-DD):
+ +
+ + diff --git a/My-Personal-Journal/templates/search_results.html b/My-Personal-Journal/templates/search_results.html new file mode 100644 index 0000000..dbb709d --- /dev/null +++ b/My-Personal-Journal/templates/search_results.html @@ -0,0 +1,22 @@ + + + + Search Results + + + + +

Journal Entries

+ +
+ {% for result in results %} +

Date: {{ result[2] }}, Mood: {{ result[3] }}, Tags: {{ result[5] }}

+

{{ result[4] }}

+
+ {% else %} +

No entries found.

+ {% endfor %} +
+ + + diff --git a/My-Personal-Journal/templates/tags.html b/My-Personal-Journal/templates/tags.html new file mode 100644 index 0000000..7b3f80a --- /dev/null +++ b/My-Personal-Journal/templates/tags.html @@ -0,0 +1,18 @@ + + + + Tags + + + + +

Tags

+
    + {% for tag in tags %} +
  • {{ tag }}
  • + {% else %} +
  • No tags found.
  • + {% endfor %} +
+ + diff --git a/My-Personal-Journal/ui.py b/My-Personal-Journal/ui.py new file mode 100644 index 0000000..16ac9dd --- /dev/null +++ b/My-Personal-Journal/ui.py @@ -0,0 +1,162 @@ +# from main import add_entry, search_entries +# import datetime + +# def display_menu(): +# print("\n=== My Personal Journal ===") +# print("1. Create New Journal Entry") +# print("2. Search Journal Entries") +# print("3. Exit") + +# def get_mood(): +# mood = None +# while mood not in ["happy", "sad", "neutral"]: +# mood = input("How are you feeling today? (happy, sad, neutral): ").lower() +# return mood + +# def get_tags(): +# tags = input("Add tags (comma-separated, e.g., work, personal): ") +# return tags + +# def create_new_entry(user_id): +# mood = get_mood() +# content = input("Write your journal entry: ") +# tags = get_tags() +# add_entry(user_id, mood, content, tags) +# print("Your entry has been saved!") + +# def search_journal_entries(user_id): +# print("\nSearch Entries") +# print("1. Search by keyword") +# print("2. Search by date") +# choice = input("Choose a search option: ") + +# if choice == '1': +# search_term = input("Enter a keyword to search for: ") +# results = search_entries(user_id, search_term=search_term) +# elif choice == '2': +# date_str = input("Enter a date (YYYY-MM-DD) to search: ") +# try: +# date = datetime.datetime.strptime(date_str, '%Y-%m-%d') +# except ValueError: +# print("Invalid date format. Please use YYYY-MM-DD.") +# return +# results = search_entries(user_id, date=date) +# else: +# print("Invalid option.") +# return + +# if results: +# for entry in results: +# print(f"\nDate: {entry[2]}") +# print(f"Mood: {entry[3]}") +# print(f"Tags: {entry[5]}") +# print(f"Entry: {entry[4]}") +# print("-" * 40) +# else: +# print("No entries found for your search criteria.") + +# def main_ui(): +# user_id = input("Enter your user ID to start: ") + +# while True: +# display_menu() +# choice = input("Select an option (1-3): ") + +# if choice == '1': +# create_new_entry(user_id) +# elif choice == '2': +# search_journal_entries(user_id) +# elif choice == '3': +# print("Goodbye!") +# break +# else: +# print("Invalid choice. Please try again.") + +# if __name__ == "__main__": +# main_ui() +import tkinter as tk +from tkinter import messagebox +from journal_app import add_entry, search_entries +from datetime import datetime + +class JournalApp(tk.Tk): + def __init__(self): + super().__init__() + + self.title("Personal Journal") + self.geometry("400x400") + + self.label = tk.Label(self, text="Personal Journal", font=("Helvetica", 16)) + self.label.pack(pady=10) + + self.add_button = tk.Button(self, text="Add New Entry", command=self.add_entry) + self.add_button.pack(pady=5) + + self.search_button = tk.Button(self, text="Search Entries", command=self.search_entries) + self.search_button.pack(pady=5) + + def add_entry(self): + self.new_window = tk.Toplevel(self) + self.new_window.title("Add New Entry") + + mood_label = tk.Label(self.new_window, text="Mood (happy, sad, neutral):") + mood_label.pack(pady=5) + self.mood_entry = tk.Entry(self.new_window) + self.mood_entry.pack(pady=5) + + content_label = tk.Label(self.new_window, text="Write your journal entry:") + content_label.pack(pady=5) + self.content_entry = tk.Text(self.new_window, height=5, width=30) + self.content_entry.pack(pady=5) + + tags_label = tk.Label(self.new_window, text="Tags (comma-separated):") + tags_label.pack(pady=5) + self.tags_entry = tk.Entry(self.new_window) + self.tags_entry.pack(pady=5) + + submit_button = tk.Button(self.new_window, text="Submit", command=self.save_entry) + submit_button.pack(pady=10) + + def save_entry(self): + mood = self.mood_entry.get() + content = self.content_entry.get("1.0", tk.END) + tags = self.tags_entry.get() + + if mood and content: + add_entry("user_1", mood, content, tags) + messagebox.showinfo("Success", "Journal entry saved!") + self.new_window.destroy() + else: + messagebox.showwarning("Input Error", "Please fill in all fields.") + + def search_entries(self): + self.search_window = tk.Toplevel(self) + self.search_window.title("Search Entries") + + search_label = tk.Label(self.search_window, text="Search by keyword or date (YYYY-MM-DD):") + search_label.pack(pady=5) + self.search_entry = tk.Entry(self.search_window) + self.search_entry.pack(pady=5) + + search_button = tk.Button(self.search_window, text="Search", command=self.perform_search) + search_button.pack(pady=10) + + def perform_search(self): + search_term = self.search_entry.get() + results = search_entries("user_1", search_term=search_term) + + if results: + result_window = tk.Toplevel(self) + result_window.title("Search Results") + + for result in results: + result_label = tk.Label(result_window, text=f"Date: {result[2]}, Mood: {result[3]}, Tags: {result[5]}") + result_label.pack() + content_label = tk.Label(result_window, text=f"Entry: {result[4]}") + content_label.pack(pady=5) + else: + messagebox.showinfo("No Results", "No journal entries found.") + +if __name__ == "__main__": + app = JournalApp() + app.mainloop()