Skip to main content
Tweeted twitter.com/StackCodeReview/status/969655717035741185
edited tags; edited title
Link
200_success
  • 144.2k
  • 22
  • 188
  • 473

Overcomplicated To Do List for Programming-do list web application

typo
Source Link
jkd
  • 275
  • 1
  • 3
  • 8
from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
import SocketServer

startData = "###DATAGOESHERE###"
endData = "###ENDDATAGOESHERE###"

def readFile(fileName):
    with open(fileName,'r') as f:
        data = f.read()
        f.close()
    return data

def replaceWithData(string, data):
    data = data.strip()
    if data.strip() == "":
        return string.replace(startData, "").replace(endData, "")
    else:
        start = string.find(startData)
        end = string.find(endData)+len(endData)
        string = string[:start]+data+string[end:]
        return string

def writeFile(fileName, data):
    with open(fileName,'w') as f:
        f.write(data)
        f.close()

def serveStaticApp(request, url):
    data = readFile('data.html')
    html = readFile('features'main.html')
    html = replaceWithData(html, data)
    request.wfile.write(html)

def serverStaticFile(request, url):
    data = readFile(url)
    request.wfile.write(data)

def saveData(request, url):
    length = int(request.headers.getheader('content-length'))
    data = request.rfile.read(length).strip()
    writeFile('data.html', data)

funcMap = {
    "app": serveStaticApp,
    "main.js":serverStaticFile,
    "save": saveData,
    "main.css": serverStaticFile,
}

def handleRequest(request):
    request._set_headers()
    for i in funcMap.keys():
        if request.path[-len(i):] == i:
            funcMap[i](request, i)
            return
    request.wfile.write("<html><body><h1>REQUEST!</h1></body></html>")

class S(BaseHTTPRequestHandler):
    def _set_headers(self):
        self.send_response(200)
        self.send_header('Content-type', 'text/html')
        self.end_headers()

    def do_GET(self):
        handleRequest(self)

    def do_HEAD(self):
        self._set_headers()
        
    def do_POST(self):
        handleRequest(self)
        
        
def run(server_class=HTTPServer, handler_class=S, port=8080):
    server_address = ('localhost', port)
    httpd = server_class(server_address, handler_class)
    print 'Starting...'
    httpd.serve_forever()

if __name__ == "__main__":
    from sys import argv

    if len(argv) == 2:
        run(port=int(argv[1]))
    else:
        run()

To run it, put all the files into the same directory and run the Python file. You also need a blank file named data.html in the same directory. Then visit http://localhost:8080/app to access the app. 

You can put a small title for the task and a longer description of the task and click SAVE NEW. SAVE OVER will save over whatever one you have selected. The two arrow buttons move the task between columns. It is important to note that the webserver should not be run over the internet.

from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
import SocketServer

startData = "###DATAGOESHERE###"
endData = "###ENDDATAGOESHERE###"

def readFile(fileName):
    with open(fileName,'r') as f:
        data = f.read()
        f.close()
    return data

def replaceWithData(string, data):
    data = data.strip()
    if data.strip() == "":
        return string.replace(startData, "").replace(endData, "")
    else:
        start = string.find(startData)
        end = string.find(endData)+len(endData)
        string = string[:start]+data+string[end:]
        return string

def writeFile(fileName, data):
    with open(fileName,'w') as f:
        f.write(data)
        f.close()

def serveStaticApp(request, url):
    data = readFile('data.html')
    html = readFile('features.html')
    html = replaceWithData(html, data)
    request.wfile.write(html)

def serverStaticFile(request, url):
    data = readFile(url)
    request.wfile.write(data)

def saveData(request, url):
    length = int(request.headers.getheader('content-length'))
    data = request.rfile.read(length).strip()
    writeFile('data.html', data)

funcMap = {
    "app": serveStaticApp,
    "main.js":serverStaticFile,
    "save": saveData,
    "main.css": serverStaticFile,
}

def handleRequest(request):
    request._set_headers()
    for i in funcMap.keys():
        if request.path[-len(i):] == i:
            funcMap[i](request, i)
            return
    request.wfile.write("<html><body><h1>REQUEST!</h1></body></html>")

class S(BaseHTTPRequestHandler):
    def _set_headers(self):
        self.send_response(200)
        self.send_header('Content-type', 'text/html')
        self.end_headers()

    def do_GET(self):
        handleRequest(self)

    def do_HEAD(self):
        self._set_headers()
        
    def do_POST(self):
        handleRequest(self)
        
        
def run(server_class=HTTPServer, handler_class=S, port=8080):
    server_address = ('localhost', port)
    httpd = server_class(server_address, handler_class)
    print 'Starting...'
    httpd.serve_forever()

if __name__ == "__main__":
    from sys import argv

    if len(argv) == 2:
        run(port=int(argv[1]))
    else:
        run()

To run it, put all the files into the same directory and run the Python file. Then visit http://localhost:8080/app to access the app. You can put a small title for the task and a longer description of the task and click SAVE NEW. SAVE OVER will save over whatever one you have selected. The two arrow buttons move the task between columns. It is important to note that the webserver should not be run over the internet.

from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
import SocketServer

startData = "###DATAGOESHERE###"
endData = "###ENDDATAGOESHERE###"

def readFile(fileName):
    with open(fileName,'r') as f:
        data = f.read()
        f.close()
    return data

def replaceWithData(string, data):
    data = data.strip()
    if data.strip() == "":
        return string.replace(startData, "").replace(endData, "")
    else:
        start = string.find(startData)
        end = string.find(endData)+len(endData)
        string = string[:start]+data+string[end:]
        return string

def writeFile(fileName, data):
    with open(fileName,'w') as f:
        f.write(data)
        f.close()

def serveStaticApp(request, url):
    data = readFile('data.html')
    html = readFile('main.html')
    html = replaceWithData(html, data)
    request.wfile.write(html)

def serverStaticFile(request, url):
    data = readFile(url)
    request.wfile.write(data)

def saveData(request, url):
    length = int(request.headers.getheader('content-length'))
    data = request.rfile.read(length).strip()
    writeFile('data.html', data)

funcMap = {
    "app": serveStaticApp,
    "main.js":serverStaticFile,
    "save": saveData,
    "main.css": serverStaticFile,
}

def handleRequest(request):
    request._set_headers()
    for i in funcMap.keys():
        if request.path[-len(i):] == i:
            funcMap[i](request, i)
            return
    request.wfile.write("<html><body><h1>REQUEST!</h1></body></html>")

class S(BaseHTTPRequestHandler):
    def _set_headers(self):
        self.send_response(200)
        self.send_header('Content-type', 'text/html')
        self.end_headers()

    def do_GET(self):
        handleRequest(self)

    def do_HEAD(self):
        self._set_headers()
        
    def do_POST(self):
        handleRequest(self)
        
        
def run(server_class=HTTPServer, handler_class=S, port=8080):
    server_address = ('localhost', port)
    httpd = server_class(server_address, handler_class)
    print 'Starting...'
    httpd.serve_forever()

if __name__ == "__main__":
    from sys import argv

    if len(argv) == 2:
        run(port=int(argv[1]))
    else:
        run()

To run it, put all the files into the same directory and run the Python file. You also need a blank file named data.html in the same directory. Then visit http://localhost:8080/app to access the app. 

You can put a small title for the task and a longer description of the task and click SAVE NEW. SAVE OVER will save over whatever one you have selected. The two arrow buttons move the task between columns. It is important to note that the webserver should not be run over the internet.

Source Link
jkd
  • 275
  • 1
  • 3
  • 8

Overcomplicated To Do List for Programming

I personally find it helpful to see what aspects of a program I already implemented and what features I need to add. Just for fun, I decided to write up a program to do that. This is the first program for which I tried to write clean looking code. Sorry for the long, overcomplicated code.

The way the program stores/retrieves data is weird and inefficient mainly because I don't know a better way.

The program is html and javascript communicating to a Python webserver. I also hosted the program on the webserver to make cross origin stuff easier to deal with. Unfortunately all the filenames are hardcoded into the program.

webserver.py:

from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
import SocketServer

startData = "###DATAGOESHERE###"
endData = "###ENDDATAGOESHERE###"

def readFile(fileName):
    with open(fileName,'r') as f:
        data = f.read()
        f.close()
    return data

def replaceWithData(string, data):
    data = data.strip()
    if data.strip() == "":
        return string.replace(startData, "").replace(endData, "")
    else:
        start = string.find(startData)
        end = string.find(endData)+len(endData)
        string = string[:start]+data+string[end:]
        return string

def writeFile(fileName, data):
    with open(fileName,'w') as f:
        f.write(data)
        f.close()

def serveStaticApp(request, url):
    data = readFile('data.html')
    html = readFile('features.html')
    html = replaceWithData(html, data)
    request.wfile.write(html)

def serverStaticFile(request, url):
    data = readFile(url)
    request.wfile.write(data)

def saveData(request, url):
    length = int(request.headers.getheader('content-length'))
    data = request.rfile.read(length).strip()
    writeFile('data.html', data)

funcMap = {
    "app": serveStaticApp,
    "main.js":serverStaticFile,
    "save": saveData,
    "main.css": serverStaticFile,
}

def handleRequest(request):
    request._set_headers()
    for i in funcMap.keys():
        if request.path[-len(i):] == i:
            funcMap[i](request, i)
            return
    request.wfile.write("<html><body><h1>REQUEST!</h1></body></html>")

class S(BaseHTTPRequestHandler):
    def _set_headers(self):
        self.send_response(200)
        self.send_header('Content-type', 'text/html')
        self.end_headers()

    def do_GET(self):
        handleRequest(self)

    def do_HEAD(self):
        self._set_headers()
        
    def do_POST(self):
        handleRequest(self)
        
        
def run(server_class=HTTPServer, handler_class=S, port=8080):
    server_address = ('localhost', port)
    httpd = server_class(server_address, handler_class)
    print 'Starting...'
    httpd.serve_forever()

if __name__ == "__main__":
    from sys import argv

    if len(argv) == 2:
        run(port=int(argv[1]))
    else:
        run()

main.html

<!DOCTYPE>
<html>
<head>
    <link rel="stylesheet" href="main.css" type="text/css">
</head>
<body>
    <div>
        <span id="not-implemented-label">Not Implemented:</span>
        <span id="implemented-label">Implemented:</span>
    </div>
    <span id="save">
        ###DATAGOESHERE###
        <select id="not-implemented-select" size="2" multiple="true"></select>
        <span id="controls">
            <input type="button" value="<-" id="left-arrow-button">
            <input type="button" value="LOAD" id="load-button">
            <input type="button" value="->" id="right-arrow-button"><br>
            <input type="button" value="DELETE" id="delete-button">
        </span>
        <select id="implemented-select" size="2" multiple="true"></select>
        ###ENDDATAGOESHERE###
    </span>
    <br>
    <br>
    <input type="text" id="title-input" placeholder="Title"><br>
    <textarea id="description-input" placeholder="Description"></textarea>
    <span id="save-controls">
        <input type="button" value="SAVE OVER" id="save-over-button"><br>
        <input type="button" value="SAVE NEW" id="save-new-button"><br>
        <input type="button" value="CLEAR" id="clear-button">
    </span>
    <script src="functions.js"></script>
    <script src="main.js"></script>
</body>
</html>

main.css

select{
    width:40vw;
    height:60vh;

}
textarea{
    width:80vw;
    height:30vh;
}
#title-input{
    width:80vw;
}
#controls{
    display:inline-block;
    width:8em;
    text-align: center;
}
#save-controls{
    display:inline-block;
}
#not-implemented-label{
    display:inline-block;
    width:calc(40vw + 8em);
}
.description{
    display:none;
}

main.js

"use strict";
var mostRecentlySelectedOptions = [];
var allowTextOverrride = true;
var saveServerURL = "/save"

var cachedElements = {};
function gebi(id){
    var cachedElement = cachedElements[id];
    if(cachedElement === undefined){
        cachedElement = document.getElementById(id);
        cachedElements[id] = cachedElement;
        return cachedElement;
    }else{
        return cachedElement;
    }
}

function slice(collection){
    return [].slice.call(collection);
}

function moveOptions(options, toSelect){
    options = options.slice();
    for(var i = 0; i < options.length; i++){
        toSelect.appendChild(options[i]);
    }
    saveToServer();
}

function setInputs(title, description){
    allowTextOverrride = true;
    gebi("title-input").value = title;
    gebi("description-input").value = description;
}

function getInputs(){
    return {
        "title": gebi("title-input").value, 
        "description": gebi("description-input").value
    };
}

function getSafeInputs(){
    var inputs = getInputs();
    if(inputs.title.trim() == ""){
        inputs.title = "<untitled>";
    }
    inputs.title = inputs.title.trim();
    inputs.description = inputs.description.trim();
    return inputs;
}

function clearInputs(){
    setInputs("", "");
}

function moveSelectedOptions(toSelect){
    moveOptions(mostRecentlySelectedOptions, toSelect);
}

function onSelectionChange(target){
    mostRecentlySelectedOptions = slice(target.selectedOptions);
    if(allowTextOverrride){
        loadSelectedOption();
    }
}

function deselectAllInSelect(selectElement){
    var selected = selectElement.selectedOptions;
    for(var i = 0; i < selected.length; i++){
        selected[i].selected = false;
    }
}

function saveText(){
    allowTextOverrride = true;
    saveToServer();
}

function saveToServer(){
    var dataToSave = gebi("save").innerHTML;
    var xhttp = new XMLHttpRequest();
    xhttp.open("POST", saveServerURL, true);
    xhttp.setRequestHeader("Content-type", "text/plain");
    xhttp.send(dataToSave);
}

function removeOptions(options){
    options = options.slice();
    for(var i = 0; i < options.length; i++){
        options[i].parentNode.removeChild(options[i]);
    }
    saveToServer();
}

function removeSelectedOptions(){
    removeOptions(mostRecentlySelectedOptions);
}

function setOptionData(option, title, description){
    option.text = title;
    option.setAttribute("data-description", description);
}

function getOptionData(option){
    return {
        "title": option.text, 
        "description": option.getAttribute("data-description")
    };
}

function createOption(title, description){
    var option = document.createElement("option");
    setOptionData(option, title, description);
    return option;
}

function addOption(title, description, parentSelect){
    var option = createOption(title, description);
    parentSelect.add(option);
    deselectAllInSelect(parentSelect);
    option.selected = true;
}

function saveNew(){
    var inputData = getSafeInputs();
    var parentSelect = gebi("not-implemented-select");
    addOption(inputData.title, inputData.description, parentSelect);
    saveText();
}

function loadSelectedOption(){
    if(mostRecentlySelectedOptions.length == 1){
        var selectedOption = mostRecentlySelectedOptions[0];
        var data = getOptionData(selectedOption);
        setInputs(data.title, data.description);
    }
}

function saveOverwrite(){
    if(mostRecentlySelectedOptions.length == 1){
        var selectedOption = mostRecentlySelectedOptions[0];
        var inputData = getSafeInputs();
        setOptionData(selectedOption, inputData.title, inputData.description);
        saveText();
    }else{
        saveNew();
    }
}

function addEventListeners(){
    var notImplementedSelect = gebi("not-implemented-select");
    var implementedSelect = gebi("implemented-select");

    gebi("right-arrow-button").addEventListener("click", function(){
        moveSelectedOptions(implementedSelect);
    });

    gebi("left-arrow-button").addEventListener("click", function(){
        moveSelectedOptions(notImplementedSelect);
    });

    gebi("load-button").addEventListener("click", function(){
        loadSelectedOption();
        allowTextOverrride = false;
    });

    gebi("delete-button").addEventListener("click", function(){
        removeSelectedOptions();
    });

    gebi("save-over-button").addEventListener("click", function(){
        saveOverwrite();
    });

    gebi("save-new-button").addEventListener("click", function(){
        saveNew();
    });

    gebi("clear-button").addEventListener("click", function(){
        clearInputs();
    });

    implementedSelect.addEventListener("change", function(event){
        onSelectionChange(event.target);
    });

    implementedSelect.addEventListener("focus", function(event){
        onSelectionChange(event.target);
    });

    notImplementedSelect.addEventListener("change", function(event){
        onSelectionChange(event.target);
    });

    notImplementedSelect.addEventListener("focus", function(event){
        onSelectionChange(event.target);
    });

    gebi("title-input").addEventListener("keypress", function(){
        allowTextOverrride = false;
    });

    gebi("description-input").addEventListener("keypress", function(){
        allowTextOverrride = false;
    });

    window.addEventListener("beforeunload", function(){
        //alert();
    });
}

addEventListeners();

To run it, put all the files into the same directory and run the Python file. Then visit http://localhost:8080/app to access the app. You can put a small title for the task and a longer description of the task and click SAVE NEW. SAVE OVER will save over whatever one you have selected. The two arrow buttons move the task between columns. It is important to note that the webserver should not be run over the internet.

I'm particularly interesting in the following:

  • Are my variables/functions well named?
  • Are my functions doing too much or too little?
  • Are there cases that you can get unexpected behavior?
  • Do you feel that the code is easily extendable/maintainable?
  • What can I do about the long list of addEventListeners?