Skip to content

Commit

Permalink
feat: nodes are sortable now (#11)
Browse files Browse the repository at this point in the history
  • Loading branch information
mbarde committed Dec 22, 2023
1 parent 54e12d3 commit 6c6620c
Show file tree
Hide file tree
Showing 8 changed files with 112 additions and 9 deletions.
15 changes: 12 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,17 @@ flask init-db

Database then lives in `database.sqlite`. To reset database, delete this file and run `init-db` again.


## Migrate to v.1.0.0

(see https://github.com/mbarde/sloth-tools/issues/11)

```
export FLASK_APP=server.py
flask migrate-to-v1
```


## Run in dev mode

```
Expand Down Expand Up @@ -81,6 +92,7 @@ Enable:
sudo systemctl enable homecontrol.service
```


## API

Once running you can access Sloth Tools via the provided web application. But you can also directly interact with the backend by calling certain endpoints:
Expand All @@ -99,6 +111,3 @@ For example you could use following script to blink node 42 everytime a motion i
#!/bin/sh
wget -O/dev/null 192.168.0.13:5000/toggle?id=42 -q
```



32 changes: 32 additions & 0 deletions db.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,37 @@ def init_db():
db.executescript(f.read().decode('utf8'))


@click.command('migrate-to-v1')
@with_appcontext
def migrate_to_v1():
# version 1.0.0 introduces sorting feature for nodes,
# which depends on additional field `sort_order`
# in `node` table.
db = get_db()

cursor = db.cursor()
cursor.execute('PRAGMA table_info(node);')
columns_info = cursor.fetchall()
columns_names = [c['name'] for c in columns_info]

if 'sort_order' not in columns_names:
click.echo('Adding missing field `sort_order` to table `node`.')
cursor.execute('ALTER TABLE `node` ADD `sort_order` INTEGER NOT NULL;')

cursor.execute('CREATE UNIQUE INDEX `idx_unique_sort_order` ON `node` (`sort_order`);')

click.echo('Initializing new field `sort_order` with node ID.')
query = 'UPDATE node SET "sort_order" = id;'
cursor.execute(query)
db.commit()

cursor.close()

# we have to stop timer manually here
# otherwise process will run forever
current_app.eventTable.stopTimer()


@click.command('init-db')
@with_appcontext
def init_db_command():
Expand All @@ -44,3 +75,4 @@ def init_db_command():
def init_app(app):
app.teardown_appcontext(close_db)
app.cli.add_command(init_db_command)
app.cli.add_command(migrate_to_v1)
10 changes: 10 additions & 0 deletions nodes.html
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,16 @@
<button title="Delete node" onclick="deleteNode(event, {{ node.id }}, '{{ node.title }}')" class="transparent">🗑️</button>
<button title="Edit node" onclick="showPopupForm('node', event, {{ node.id }})" class="transparent">🖊️</button>
<button title="Edit events" onclick="showEventList({{ node.id }}, event)" class="transparent">📅</button>
{% if loop.index > 1 %}
<button title="Move node up" onclick="reorderNodes({{ node.sort_order }}, {{ node.sort_order }} - 1)" class="transparent"></button>
{% else %}
<button class="invisible"></button>
{% endif %}
{% if loop.index < nodes | length %}
<button title="Move node down" onclick="reorderNodes({{ node.sort_order }}, {{ node.sort_order }} + 1)" class="transparent"></button>
{% else %}
<button class="invisible"></button>
{% endif %}
<div>
</li>
{% endfor %}
Expand Down
31 changes: 30 additions & 1 deletion server.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,8 @@ def toggle():
@app.route('/nodes')
def nodes():
nodeService = CRUDService('node')
nodes = nodeService.read()
nodes = nodeService.readAll(
order_by='sort_order', order_mode='ASC')
return render_template('./nodes.html', nodes=nodes)

@app.route('/node/create', methods=['GET', 'POST'])
Expand All @@ -150,6 +151,15 @@ def nodeCreate():

if jsonData is not None:
node = jsonData

# get currently highest sort_order value
allNodes = nodeService.readAll(order_by='sort_order', order_mode='DESC')
if len(allNodes) > 0:
# to make sure new node is appended at the end
node['sort_order'] = allNodes[0]['sort_order'] + 1
else:
node['sort_order'] = 0

if nodeService.create(node):
return 'OK'

Expand Down Expand Up @@ -196,6 +206,25 @@ def nodeDelete(id):
nodeService.delete(id)
return 'OK'

@app.route('/node/reorder/<int:posFrom>/<int:posTo>', methods=['POST'])
def nodeReorder(posFrom, posTo):
# flips sort_order positions of two nodes
nodeService = CRUDService('node')

nodeFrom = dict(nodeService.readBy('sort_order', posFrom)[0])
nodeTo = dict(nodeService.readBy('sort_order', posTo)[0])

nodeTo['sort_order'] = -1
nodeService.update(nodeTo)

nodeFrom['sort_order'] = posTo
nodeService.update(nodeFrom)

nodeTo['sort_order'] = posFrom
nodeService.update(nodeTo)

return 'OK'

# event API
@app.route('/event/create/<int:nodeId>', methods=['GET', 'POST'])
def eventCreate(nodeId):
Expand Down
11 changes: 10 additions & 1 deletion service.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,12 @@ def create(self, data):
conn.commit()
return True

def readAll(self):
def readAll(self, order_by=None, order_mode='ASC'):
conn = db.get_db()
sql = 'SELECT * FROM {0}'.format(self.tableName)
if order_by is not None:
sql += ' ORDER BY {0} {1};'.format(
order_by, order_mode)
results = conn.execute(sql).fetchall()
return results

Expand Down Expand Up @@ -100,3 +103,9 @@ def delete(self, id):
conn.execute(sql, (id,))
conn.commit()
return True

def countRows(self):
conn = db.get_db()
query = 'SELECT COUNT(*) FROM {0};'.format(self.tableName)
conn.execute(query)
return conn.fetchone()[0]
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@

setup(
name='sloth-tools',
version='0.9.0',
version='1.0.0',
)
10 changes: 7 additions & 3 deletions static/css/classic/custom.css
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,10 @@ button.transparent {
background: none;
}

button.invisible {
visibility: hidden;
}

#jobs-container {
background: rgba(0.2, 0.2, 0.2, 0.2);
color: black;
Expand Down Expand Up @@ -123,7 +127,7 @@ ul.nodes {
ul.nodes li.node {
margin-bottom: 15px;
overflow: hidden;
width: calc(100% + 142px);
width: calc(100% + 229px);
}

ul.nodes li.node.disabled {
Expand Down Expand Up @@ -215,14 +219,14 @@ li.event ul.weekdays li:hover {
background: transparent;
}
to {
transform: translateX(-142px);
transform: translateX(-229px);
background: #D5ECC9;
}
}

@keyframes slideback {
from {
transform: translateX(-142px);
transform: translateX(-229px);
background: #D5ECC9;
}
to {
Expand Down
10 changes: 10 additions & 0 deletions static/js/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,16 @@ function deleteNode(clickEvent, nodeId, nodeTitle) {
return false
}

function reorderNodes(posFrom, posTo) {
var url = '/node/reorder/' + posFrom + '/' + posTo
var xhttp = new XMLHttpRequest()
xhttp.open('POST', url)
xhttp.addEventListener('load', function() {
refreshNodes()
})
xhttp.send()
}

function onToolClicked(element, event) {
event.preventDefault()
event.stopPropagation()
Expand Down

0 comments on commit 6c6620c

Please sign in to comment.