diff --git a/process_keep.py b/process_keep.py new file mode 100644 index 0000000..14464d6 --- /dev/null +++ b/process_keep.py @@ -0,0 +1,306 @@ +import os +import json + +""" This was written almost entirely by Google Gemini 2.5 (apps and code) on 18 Sept. 2025 + +Google Takeout is accessible from the browser when logged in to your Google account. +It can be set up to export dozens of different types of data. Here we only export "Keep" +data. +After running this, move the generated file to expoweb/handbook/troggle +and git add/commit/push +Philip Sargent +""" +# --- CONFIGURATION --- +# šŸ“Œ IMPORTANT: Replace this with the actual path to your Google Keep Takeout folder. +TAKEOUT_DIRECTORY = 'Takeout/Keep' # Example for Linux/macOS +# + +def process_keep_files_to_dict(directory_path): + """ + Scans a directory for Google Keep JSON files, filters for a specific + label, prints the content, and returns the data as a list of dictionaries. + + Args: + directory_path (str): The path to the folder containing the JSON files. + + Returns: + list: A list of dictionaries, where each dictionary represents a note. + """ + print(f"šŸ“ Processing files in: {directory_path}\n") + + # This list will store the dictionary for each matching note + extracted_notes = [] + + if not os.path.isdir(directory_path): + print(f"āŒ Error: Directory not found at '{directory_path}'.") + print("Please update the TAKEOUT_DIRECTORY variable with the correct path.") + return extracted_notes + + # Iterate over every file in the specified directory + for filename in os.listdir(directory_path): + if filename.endswith('.json'): + file_path = os.path.join(directory_path, filename) + + try: + with open(file_path, 'r', encoding='utf-8') as f: + data = json.load(f) + + # --- Label Check --- + has_expo_label = False + if 'labels' in data: + if any(label.get('name', '').lower() == 'expo' for label in data['labels']): + has_expo_label = True + + if not has_expo_label: + continue # Skip this file + + # --- Data Extraction & Storage --- + + # Create a dictionary to hold the current note's data + current_note = { + 'title': data.get('title') or 'Untitled Note', + 'source_file': filename, + 'content_type': None, + 'color': data.get('color', 'DEFAULT'), + 'content': None + } + + # As before, print to the console for immediate feedback + print("-" * 40) + print(f"āœ… Title: {current_note['title']}") + + # Handle text notes + if 'textContent' in data: + content = data['textContent'] + current_note['content_type'] = 'text' + current_note['content'] = content + print(content) + + # Handle list notes + elif 'listContent' in data: + current_note['content_type'] = 'list' + list_items = [] + checked_items = [] + unchecked_items = [] + # print("Tasks:") + for item in data['listContent']: + # Create a clean dictionary for the list item + item_data = { + 'text': item.get('text', ''), + 'is_checked': item.get('isChecked', False) + } + list_items.append(item_data) + if item_data['is_checked']: + checked_items.append(item_data) + else: + unchecked_items.append(item_data) + # Print the list item to the console + # status = 'x' if item_data['is_checked'] else ' ' + # print(f" [{status}] {item_data['text']}") + print(f"Tasks: {len(list_items)} of which {len(checked_items)} are done.") + current_note['content'] = list_items + current_note['unchecked'] = unchecked_items + + # Add the completed dictionary for this note to our main list + extracted_notes.append(current_note) + + # print("-" * 40 + "\n") + + except Exception as e: + print(f"ā—ļø An unexpected error occurred with file {filename}: {e}") + + if not extracted_notes: + print("No notes with the 'EXPO' label were found in the directory.") + sorted_notes = sorted(extracted_notes, key=lambda note: len(note['unchecked'])) + + return sorted_notes + +import html +from datetime import datetime + +def write_html_output(notes_data, filename="keep_export.html"): + """ + Generates an HTML file from the extracted Keep notes data. + + Args: + notes_data (list): A list of note dictionaries. + filename (str): The name of the output HTML file. + """ + + # These are the standard Google Keep colors. + color_map = { + "DEFAULT": "#ffffff", + "RED": "#f28b82", + "ORANGE": "#fbbc04", + "YELLOW": "#fff475", + "GREEN": "#ccff90", + "TEAL": "#a7ffeb", + "BLUE": "#cbf0f8", + "DARK_BLUE": "#aecbfa", + "PURPLE": "#d7aefb", + "PINK": "#fdcfe8", + "BROWN": "#e6c9a8", + "GRAY": "#e8eaed", + } + + # --- HTML Head and CSS Styling --- + # The CSS is embedded directly in the HTML file for simplicity. + html_head = f""" + + + + + + Google Keep Export + + +""" + + # --- HTML Body Generation --- + html_body_content = "" + for note in notes_data: + # Sanitize title to prevent HTML injection issues + title = html.escape(note['title']) + + # Start building the content for this specific note + note_content_html = "" + + # MODIFICATION 2: Get the color and apply it as an inline style + note_color_name = note.get('color', 'DEFAULT') + # Safely get the color from the map, falling back to the default white + bg_color = color_map.get(note_color_name, color_map['DEFAULT']) + + # Handle text notes + if note['content_type'] == 'text': + # Sanitize text content and replace newlines with
tags + text = html.escape(note['content']) + note_content_html = f'

{text}

' + + # Handle list notes + elif note['content_type'] == 'list': + unchecked_items = [] + checked_items = [] + + for item in note['content']: + # Sanitize list item text + item_text = html.escape(item['text']) + if item['is_checked']: + checked_items.append( + f'
  • {item_text}
  • ' + ) + else: + unchecked_items.append( + f'
  • {item_text}
  • ' + ) + + # Combine unchecked items + note_content_html += '' + + # Add checked items inside a collapsible
    element + if checked_items: + item_count = len(checked_items) + plural_s = 's' if item_count > 1 else '' + note_content_html += f""" +
    + {item_count} completed item{plural_s} + +
    + """ + + # Combine title and content into a single note card + html_body_content += f""" +
    +

    {title}

    + {note_content_html} +
    + """ + # --- Final HTML Assembly --- + full_html = f""" +{html_head} + +

    Troggle coding and design to-do lists

    +

    (Exported from Philip Sargent's Google Keep notes on {datetime.now().strftime('%Y-%m-%d %H:%M')}) +

    Contact Philip if you wish to share these lists on your Google Keep account. +

    + {html_body_content} +
    + + +""" + + # --- Writing to File --- + try: + with open(filename, 'w', encoding='utf-8') as f: + f.write(full_html) + print(f"\nšŸŽ‰ Successfully wrote {len(notes_data)} notes to '{filename}'") + except Exception as e: + print(f"ā—ļø An error occurred while writing the file: {e}") + +# --- Run the program --- +if __name__ == "__main__": + all_notes_data = process_keep_files_to_dict(TAKEOUT_DIRECTORY) + + if all_notes_data: + print("\n" + "="*50) + print("šŸŽ‰ Successfully processed all matching files.") + print(f"Total notes extracted: {len(all_notes_data)}") + # print("Data has been stored in the 'all_notes_data' variable.") + + print("="*50) + write_html_output(all_notes_data) \ No newline at end of file