Help With Creating Task and Creating JSON

Question:
So I made an extension to be able to create, delete, and save tasks. I realized the JSON I was using, was being used for everyone, so I tried to make it so each person had their own JSON, or at the very least tasks under their username that the extension would use. I wasn’t able to get that working, and in the process of doing that creating tasks isn’t working. I am not getting any errors in my code either.

Repl link:
https://replit.com/@SalladShooter/To-Do-Extension

Flask →

from flask import Flask, render_template, request, jsonify
import json
import os

app = Flask(__name__)

def get_replit_username():
    return os.environ.get("REPLIT_USER", "default_username")

def get_json_file_path(username):
    return f'database/{username}_data.json'

def load_tasks(username):
    json_file_path = get_json_file_path(username)
    try:
        with open(json_file_path, 'r') as file:
            tasks = json.load(file)
    except FileNotFoundError:
        tasks = {'tasks': []}
    return tasks.get('tasks', [])

def save_tasks(username, tasks):
    json_file_path = get_json_file_path(username)
    with open(json_file_path, 'w') as file:
        json.dump({'tasks': tasks}, file) 

@app.route('/tasks', methods=['GET'])
def get_tasks():
    username = get_replit_username()
    tasks = load_tasks(username)
    return jsonify(tasks)

@app.route('/', methods=['GET', 'POST'])
def index():
    username = get_replit_username()

    if request.method == 'POST':
        task_data = request.form.get('task_data')
        if task_data:
            tasks = load_tasks(username)
            tasks.append({'taskText': task_data, 'completed': False})
            save_tasks(username, tasks)
            return jsonify({'status': 'success', 'message': 'Task added'})

    tasks = load_tasks(username)
    return render_template('index.html', tasks=tasks)

@app.route('/delete_task', methods=['POST'])
def delete_task():
    username = get_replit_username()
    task_data = request.form.get('task_data')
    if task_data:
        tasks = load_tasks(username)
        if task_data in [task['taskText'] for task in tasks]:
            tasks = [task for task in tasks if task['taskText'] != task_data]
            save_tasks(username, tasks)
            return jsonify({'status': 'success', 'message': 'Task deleted'})

    return jsonify({'status': 'error', 'message': 'Task not found'})

@app.route('/update_task', methods=['POST'])
def update_task():
    username = get_replit_username()
    task_data = request.form.get('task_data')
    completed = request.form.get('completed') == 'true'
    if task_data:
        tasks = load_tasks(username)
        for task in tasks:
            if task['taskText'] == task_data:
                task['completed'] = completed
                save_tasks(username, tasks)
                return jsonify({'status': 'success', 'message': 'Task updated'})

    return jsonify({'status': 'error', 'message': 'Task not found'})

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000)

JS →

document.addEventListener('DOMContentLoaded', function () {
    var createTaskButton = document.querySelector('.create-task');
    var taskList = document.querySelector('.task-list');

    createTaskButton.addEventListener('click', function () {
        var taskText = prompt('Enter task name:');
        if (taskText) {
            var formData = new URLSearchParams();
            formData.append('task_data', taskText);

            fetch('/', {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/x-www-form-urlencoded',
                },
                body: formData,
            })
            .then(response => response.json())
            .then(data => {
                console.log('Server response:', data);
                addTask(taskText, false);
            });
        }
    });

    function addTask(taskText, completed) {
        var taskElement = document.createElement('div');
        taskElement.className = 'task';

        var taskTextElement = document.createElement('span');
        taskTextElement.textContent = taskText;

        var cancelIconElement = document.createElement('span');
        cancelIconElement.className = 'material-symbols-outlined cancel-icon';
        cancelIconElement.textContent = 'cancel';

        if (completed) {
            taskElement.classList.add('completed');
        }

        taskElement.addEventListener('click', function () {
            taskElement.classList.toggle('completed');
            updateTaskStatus(taskText, taskElement.classList.contains('completed'));
        });

        function updateTaskStatus(taskText, completed) {
            fetch('/update_task', {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/x-www-form-urlencoded',
                },
                body: 'task_data=' + encodeURIComponent(taskText) +
                    '&completed=' + completed,
            })
                .then(response => response.json())
                .then(data => {
                    console.log('Server response:', data);
                });
        }

        function deleteTask(taskText) {
            fetch('/delete_task', {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/x-www-form-urlencoded',
                },
                body: 'task_data=' + encodeURIComponent(taskText),
            })
                .then(response => response.json())
                .then(data => {
                    console.log('Server response:', data);
                });
        }

        cancelIconElement.addEventListener('click', function (event) {
            event.stopPropagation();
            taskList.removeChild(taskElement);
        });

        taskElement.appendChild(taskTextElement);
        taskElement.appendChild(cancelIconElement);
        taskList.appendChild(taskElement);
    }

        cancelIconElement.addEventListener('click', function (event) {
            event.stopPropagation();
            taskList.removeChild(taskElement);

        taskElement.appendChild(taskTextElement);
        taskElement.appendChild(cancelIconElement);
        taskList.appendChild(taskElement);
    }

    createTaskButton.addEventListener('click', function () {
        var taskText = prompt('Enter task name:');
        if (taskText) {
            var formData = new URLSearchParams();
            formData.append('task_data', taskText);

            fetch('/', {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/x-www-form-urlencoded',
                },
                body: formData,
            })
            .then(response => response.json())
            .then(data => {
                console.log('Server response:', data);
                addTask(taskText, false);
            });
        }
    });


    fetch('/tasks')
        .then(response => response.json())
        .then(tasks => {
            tasks.forEach(task => {
                addTask(task.taskText, task.completed);
            });
        });
}); 

Try to add some logging to your code just to see if the requests are reaching the server. It’s kinda difficult tell what it is without any direction.

In your flask add the logging:

import logging

logging.basicConfig(level=logging.INFO)

And in your rote (still your Flask) add the handling:

@app.route('/', methods=['GET', 'POST'])
def index():
    logging.info(f"Request method: {request.method}")

Another thing is that in your JS You are not calling the deleteTask function inside your addTask anywhere. So, the tasks aren’t being actually deleted. I’d suggest call the function inside the cancelIconElement event:

cancelIconElement.addEventListener('click', function (event) {
    event.stopPropagation();
    deleteTask(taskText);
    taskList.removeChild(taskElement);
});

Run the code again and tell me the logging results.

2 Likes

@WindLother I can create a task now, but for some reason it asks for the taskText twice when using the extension. Also the Flask did create a JSON but not with my username.

Logging Results →

* Serving Flask app 'main'
* Debug mode: off
INFO:werkzeug :WARNING: This is a developmen t server. Do not use it in a production dep Loyment. Use a production WSGI server inste ad.
* Running on all addresses (0.0.0.0)
* Running on http://127.0.0.1:5000
* Running on http://172.31.196.34:5000
INFO:werkzeug:Press CTRL+C to quit
INFO: werkzeug:172.31.196.1 - - [29/Dec/2023
14:33:24] "GET /extension. json HTTP/1.1" 4
04 -
INFO: root:Request method: GET
INFO: werkzeug:172.31.196.1 - - [29/Dec/2023
14:33:25] "GET / HTTP/1.1" 200 -
INFO: werkzeug:172.31.196.1 - - [29/Dec/2023
14:33:25] "GET / static/style.css HTTP/1.1"
304 -
INFO:werkzeug:172.31.196.1 - - [29/Dec/2023
14:33:25] "GET /static/script.js HTTP/1.1"
200 -
INFO:werkzeug:172.31.196.1 - - [29/Dec/2023
14:33:25] "GET /tasks HTTP/1.1" 200 -
INFO: root:Request method: POST
INFO:werkzeug:172.31.196.1 - - [29/Dec/2023
14:33:29] "POST / HTTP/1.1" 200 -
INFO: root: Request method: POST
INFO: werkzeug:172.31.196.1 - - [29/Dec/2023
14:33:33] "POST / HTTP/1.1" 200 -
INFO:werkzeug:172.31.196.1 - - [29/Dec/2023
14:33:44] "POST /delete_task HTTP/1.1" 200 

You have a duplicate event in your js

Remove this part and should be ok:

createTaskButton.addEventListener('click', function () {
    var taskText = prompt('Enter task name:');
    if (taskText) {
        var formData = new URLSearchParams();
        formData.append('task_data', taskText);

        fetch('/', {
            method: 'POST',
            headers: {
                'Content-Type': 'application/x-www-form-urlencoded',
            },
            body: formData,
        })
        .then(response => response.json())
        .then(data => {
            console.log('Server response:', data);
            addTask(taskText, false);
        });
    }
});



check what username is being returned

Update the get_replit_username() function

def get_replit_username():
    username = os.environ.get("REPLIT_USER", "default_username")
    logging.info(f"Retrieved username: {username}")
    return username

It gives the default_username rather than the users actual username.


Like you said I changed my JS but it still asks for the name twice (sorry if it is obvious, I don’t know too much JS) →

document.addEventListener('DOMContentLoaded', function () {
    var createTaskButton = document.querySelector('.create-task');
    var taskList = document.querySelector('.task-list');

    createTaskButton.addEventListener('click', function () {
        var taskText = prompt('Enter task name:');
        if (taskText) {
            var formData = new URLSearchParams();
            formData.append('task_data', taskText);

            fetch('/', {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/x-www-form-urlencoded',
                },
                body: formData,
            })
            .then(response => response.json())
            .then(data => {
                console.log('Server response:', data);
                addTask(taskText, false);
            });
        }
    });

    function addTask(taskText, completed) {
        var taskElement = document.createElement('div');
        taskElement.className = 'task';

        var taskTextElement = document.createElement('span');
        taskTextElement.textContent = taskText;

        var cancelIconElement = document.createElement('span');
        cancelIconElement.className = 'material-symbols-outlined cancel-icon';
        cancelIconElement.textContent = 'cancel';

        if (completed) {
            taskElement.classList.add('completed');
        }

        taskElement.addEventListener('click', function () {
            taskElement.classList.toggle('completed');
            updateTaskStatus(taskText, taskElement.classList.contains('completed'));
        });

        function updateTaskStatus(taskText, completed) {
            fetch('/update_task', {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/x-www-form-urlencoded',
                },
                body: 'task_data=' + encodeURIComponent(taskText) +
                    '&completed=' + completed,
            })
                .then(response => response.json())
                .then(data => {
                    console.log('Server response:', data);
                });
        }

        function deleteTask(taskText) {
            fetch('/delete_task', {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/x-www-form-urlencoded',
                },
                body: 'task_data=' + encodeURIComponent(taskText),
            })
                .then(response => response.json())
                .then(data => {
                    console.log('Server response:', data);
                });
        }

        cancelIconElement.addEventListener('click', function (event) {
            event.stopPropagation();
            deleteTask(taskText);
            taskList.removeChild(taskElement);
        });

        taskElement.appendChild(taskTextElement);
        taskElement.appendChild(cancelIconElement);
        taskList.appendChild(taskElement);
    }

    createTaskButton.addEventListener('click', function () {
        var taskText = prompt('Enter task name:');
        if (taskText) {
            var formData = new URLSearchParams();
            formData.append('task_data', taskText);

            fetch('/', {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/x-www-form-urlencoded',
                },
                body: formData,
            })
            .then(response => response.json())
            .then(data => {
                console.log('Server response:', data);
                addTask(taskText, false);
            });
        }
    });


    fetch('/tasks')
        .then(response => response.json())
        .then(tasks => {
            tasks.forEach(task => {
                addTask(task.taskText, task.completed);
            });
        });
}); 

Either the REPLIT_USER environment variable is not being set or is not being retrieved correctly.

You still have the duplicates in your JS

The first one:

And the second one:

Remove the entire event from the second one

1 Like

This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.