16 Commits

20 changed files with 439 additions and 67 deletions

5
.gitignore vendored
View File

@@ -1,4 +1,3 @@
# Created by https://www.toptal.com/developers/gitignore/api/python,flask,vim,visualstudiocode
# Edit at https://www.toptal.com/developers/gitignore?templates=python,flask,vim,visualstudiocode
*.db
@@ -24,7 +23,7 @@ dist/
downloads/
eggs/
.eggs/
lib/
#lib/
lib64/
parts/
sdist/
@@ -234,3 +233,5 @@ tags
# End of https://www.toptal.com/developers/gitignore/api/python,flask,vim,visualstudiocode
# deb files
*.deb

View File

@@ -219,7 +219,7 @@ If you develop a new program, and you want it to be of the greatest possible use
To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found.
flaskapp
taskmanager
Copyright (C) 2023 Decentrala
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.

View File

@@ -1,4 +1,30 @@
# flaskapp
# Task Manager
Web app
Interactive TODO list web application
# Development Setup
Install python and pip on local machine
```bash
pip install virtualenv
python -m venv venv #/path/to/new/virtual/environment
source venv/bin/activate #activate virtual env
pip install -r requirements.txt
python3 ./init_db.py #initialize database
python3 ./run.py #run project
```
# Build app
```bash
cd build-deb/
make
```
# Install app
```bash
apt install ./build-deb/taskmanager.deb
```

View File

@@ -7,8 +7,12 @@ man: man/taskmanager.1.md
deb: man ../requirments.txt ../run.py ../taskmanager ../LICENSE
cp -r ../taskmanager/* taskmanager/var/taskmanager/taskmanager/
cp ../run.py taskmanager/var/taskmanager/
cp ../init_db.py taskmanager/var/taskmanager/
cp ../LICENSE taskmanager/var/taskmanager/
chmod -w taskmanager/DEBIAN/*
chmod +w taskmanager/DEBIAN/control
dpkg-deb --build taskmanager
chmod +w taskmanager/DEBIAN/*
clean:
rm -f taskmanager.deb
rm -f man/taskmanager.1

View File

@@ -1,15 +1,15 @@
% FLASKAPP(1) taskmanager 1.0.0
% TASKMANAGER(1) taskmanager 1.0.0
% Decentrala
% Jun 2023
# NAME
taskmanager - Web app
taskmanager - Interactive TODO list Web app
# SYNOPSIS
**python3 run.py**
# DESCRIPTION
Web app
Interactive TODO list Web app
# AUTHORS
Decentrala

View File

@@ -8,4 +8,4 @@ Depends: gunicorn, python3-flask-sqlalchemy
Homepage: https://gitea.dmz.rs/Decentrala/taskmanager
Maintainer: Decentrala <dmz@dmz.rs>
Description: Web app
Version: 1.0.0
Version: 1.0.2

View File

@@ -1,3 +1,4 @@
#!/bin/sh
/usr/bin/systemctl enable taskmanager.service
/var/taskmanager/init_db.py
/sbin/service taskmanager start

View File

@@ -1,3 +1,3 @@
#!/bin/sh
/sbin/service taskmanager stop
/usr/bin/systemdctl disable taskmanager.service
/usr/bin/systemctl disable taskmanager.service

View File

@@ -0,0 +1,12 @@
[Unit]
Description=Gunicorn taskmanager service
Documentation=man:gunicorn(1)
After=network.target nss-lookup.target
[Service]
WorkingDirectory=/var/taskmanager/
ExecStart=/usr/bin/gunicorn --workers 3 --bind 0.0.0.0:80 run:app
[Install]
WantedBy=multi-user.target

View File

@@ -1,9 +1,10 @@
#!/usr/bin/env python3
from flaskapp import db
from taskmanager import db, app
print('[i] Trying to create databse...')
try:
db.create_all()
with app.app_context():
db.create_all()
print('[+] Success you can proceed with deployment!')
except:
print('[-] Creating db failed :/')

3
run.py
View File

@@ -1,4 +1,5 @@
from flaskapp import app
#!/usr/bin/env python3
from taskmanager import app
if __name__ == '__main__':
app.run(debug=False)

View File

@@ -0,0 +1,17 @@
from taskmanager.models import *
def gettaskusers(taskid):
users = list()
userids = list()
try:
taskusers = TaskUser.query.filter_by(taskid = taskid).all()
except:
taskusers = list()
for taskuser in taskusers:
userids.append(taskuser.userid)
for userid in userids:
users.append(User.query.get(userid))
return users

View File

@@ -1,7 +1,18 @@
from taskmanager import db
class Table(db.Model):
class Task(db.Model):
id = db.Column(db.Integer, primary_key=True)
variable1 = db.Column(db.Integer, nullable=False)
variable2 = db.Column(db.Integer, nullable=False)
name = db.Column(db.String, nullable=False)
desc = db.Column(db.String, nullable=True)
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String, nullable=False)
contact = db.Column(db.String, nullable=False)
password = db.Column(db.String, nullable=True)
class TaskUser(db.Model):
id = db.Column(db.Integer, primary_key=True)
taskid = db.Column(db.Integer, nullable=False)
userid = db.Column(db.Integer, nullable=False)

View File

@@ -5,30 +5,66 @@ from taskmanager.models import *
@app.route('/', methods=['GET'])
def index():
if request.method == 'GET':
localvariable1 = "Placeholder1"
localvariable1 = "Placeholder2"
try:
return render_template('index.html', pagevariable1 = localvariable1, pagevariable2 = localvariable2 )
except:
return 'Error retriving page'
else:
return 'HTTP request method not recogniezed'
tasks = Task.query.all()
return render_template('index.html', tasks = tasks)
@app.route('/submit', methods=['POST', 'GET'])
def submit():
@app.route('/addtask', methods=['GET','POST'])
def addtask():
if request.method == 'GET':
return render_template('submit.html')
return render_template('addtask.html')
elif request.method == 'POST':
userinput1 = request.form['forminput1']
userinput2 = request.form['forminput2']
sqlrow = Table(variable1 = int(userinput1), variable2 = int(userinput2))
taskname = request.form['taskname']
taskdesc = request.form['taskdesc']
sqladdtask = Task(name = taskname, desc = taskdesc)
try:
db.session.add(sqlrow)
db.session.add(sqladdtask)
db.session.commit()
return 'Row added'
return 'Task added'
except:
return 'Adding row to table failed'
return 'Adding task failed'
@app.route('/register', methods=['POST', 'GET'])
def register():
if request.method == 'GET':
return render_template('register.html')
elif request.method == 'POST':
username = request.form['username']
contact = request.form['contact']
password = request.form['password']
sqladduser = User(username = username, contact = contact, password = password)
try:
db.session.add(sqladduser)
db.session.commit()
return 'User added'
except:
return 'Adding user failed'
else:
return 'HTTP request method not recogniezed'
@app.route('/projects/<int:task_id>', methods=['GET','POST'])
def project(task_id:int):
users = gettaskusers(task_id)
if request.method == 'GET':
try:
task = Task.query.get(task_id)
except:
return 'Task not found, bad URL'
return render_template("project.html", task = task, users = users)
elif request.method == 'POST':
username = request.form['username']
if username in users:
return 'User already added to task'
try:
userid = User.query.filter_by(username = username).first().id
except:
return 'User not found, please <a href="/register">register</a>.'
sqladduser = TaskUser(userid = userid, taskid = task_id)
try:
db.session.add(sqladduser)
db.session.commit()
return 'User added'
except:
return 'Adding user failed'

View File

@@ -1,24 +1,36 @@
:root {
--border-radus: 1rem;
--background: #000000;
--header-background: #FFFFFF;
--background: #383840;
--header-background: #212121;
--header-height: 3rem;
--input-bar-height: 3rem;
--white: #FFF;
--primary: #d2d2d2;
--font-primary: #d2d2d2;
color: #FFF;
}
body{
background: var(--background);
font-family: sans-serif;
* {
margin: 0;
padding: 0;
}
a, a:visited {
color: var(--font-primary);
}
body {
background: var(--background);
font-family: sans-serif;
}
main {
display: flex;
flex-direction: column;
align-self: flex-start;
gap: 1rem;
margin: 0 auto;
padding: 30px 0;
height: calc(100vh - var(--header-height) - var(--input-bar-height));
box-sizing: border-box;
}
@@ -35,6 +47,12 @@ header {
box-sizing: border-box;
}
header ul {
display: flex;
list-style-type: none;
gap:15px
}
footer {
width: 100vw;
height: var(--input-bar-height);
@@ -46,3 +64,120 @@ footer {
box-sizing: border-box;
gap: 1rem;
}
/* global classes */
.container {
width: 100%;
max-width: 960px;
margin: 0 auto;
padding: 20px 30px;
}
.page h1 {
margin-bottom: 15px;
}
.btn-wrap {
display: flex;
align-items: center;
justify-content: center;
padding: 15px 12px;
}
.btn {
display: inline-block;
padding: 5px 20px;
border: 1px solid var(--primary);
color: var(--primary);
background-color: transparent;
border-radius: 5px;
}
.btn:hover {
background-color: rgba(0,0,0,0.3);
cursor: pointer;
}
.btn a {
text-decoration: none;
}
.label {
text-transform: uppercase;
letter-spacing: 2px;
font-size: 14px;
font-weight: 600;
margin-bottom: 11px;
display: inline-block;
user-select: none;
}
.underline {
border-bottom: 1px solid var(--font-primary);
padding-bottom: 2px;
margin-top:20px;
}
.section-tasks > ul {
margin-left: 30px;
}
/* page index*/
.page-index .btn{
margin-bottom: 20px;
}
.task-wrap {
border: 1px solid var(--primary);
padding: 20px 40px 40px 40px;
border-radius: 5px;
box-shadow: inset 0 0 9px 4px rgba(255,255,255, 0.1);
}
.task {
display: flex;
border-bottom: 1px solid white;
}
.task a:hover {
background-color: rgba(255,255,255,0.1);
}
.task a {
display: flex;
gap: 15px;
width: 100%;
padding: 20px;
text-decoration: none;
}
/* Page projects */
.page-project h1 {
text-transform: uppercase;
}
.user-info-wrap{
margin-bottom: 10px;
}
/* form */
.form-wrap {
max-width: 400px;
}
.form-input {
display: flex;
flex-direction: column;
padding: 10px 0;
}
.form-input input {
height: 25px;
border-radius: 5px;
outline: none;
}

View File

@@ -0,0 +1,37 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="stylesheet" href="/static/style.css" />
<title>Add new task</title>
</head>
<body>
<header>
<nav class="container">
<ul>
<li class="current"><a href="/">Home</a></li>
<li><a href="/register">Register</a></li>
</ul>
</nav>
</header>
<main class="container page page-addtask">
<h1>Create new task</h1>
<div class="form-wrap">
<form action="/addtask" method="POST">
<div class="form-input">
<label for="taskname" class="label">Task name:</label>
<input type="text" name="taskname" id="taskname" required />
</div>
<div class="form-input">
<label for="taskdesc" class="label">Description:</label>
<input type="text" name="taskdesc" id="taskdesc" required />
</div>
<div class="btn-wrap">
<button class="btn">Submit</button>
</div>
</div>
</form>
</main>
</body>
</html>

View File

@@ -1,19 +1,36 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Decentrala</title>
<link rel="stylesheet" href="/static/style.css">
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="stylesheet" href="/static/style.css" />
<title>Task manager</title>
</head>
<body>
<header>
<nav class="container">
<ul>
<li class="current"><a href="/">Home</a></li>
<li><a href="/register">Register</a></li>
</ul>
</nav>
</header>
<main>
<main class="container page page-index">
<section>
{% for task in tasks %}
<li>{{task}}</li>
{% endfor %}
<div class="btn">
<a href="/addtask">Add new task</a>
</div>
<div class="tasks-wrap">
<h1>Tasks</h1>
{% for task in tasks %}
<div class="task">
<a href="/projects/{{task.id}}">
<div>{{task.id}}.</div>
<div>{{task.name}}</div>
</a>
</div>
{% endfor %}
</div>
</section>
</main>
<footer>

View File

@@ -0,0 +1,53 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="stylesheet" href="/static/style.css" />
<title>{{task.name}}</title>
</head>
<body>
<header>
<nav class="container">
<ul>
<li><a href="/">Home</a></li>
<li><a href="/register">Register</a></li>
</ul>
</nav>
</header>
<main class="container page page-project">
<section >
<h1>{{task.name}}</h1>
<label class="label underline">Description</label>
<p>{{task.desc}}</p>
</section>
<section class="section-task">
<div>
<label class="label underline">Users added to this task</label>
{% for user in users %}
<div class="user-info-wrap">
<div><b>Username:</b> {{user.username}}</div>
<div><b>Contact info:</b> {{user.contact}}</div>
</div>
{% endfor %}
</div>
<div>
<label class="label underline"> Add person to task</label>
<div class="form-wrap">
<form action="/projects/{{task.id}}" method="POST">
<div class="form-input">
<label for="username" class="label">Username:</label>
<input type="text" name="username" id="username" required />
</div>
<div class="btn-wrap">
<button class="btn">Submit</button>
</div>
</form>
</div>
</div>
</section>
</main>
<footer>
</footer>
</body>
</html>

View File

@@ -0,0 +1,40 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="stylesheet" href="/static/style.css" />
<title>Register</title>
</head>
<body>
<header>
<nav class="container">
<ul>
<li><a href="/">Home</a></li>
<li class="current"><a href="/register">Register</a></li>
</ul>
</nav>
</header>
<main class="container page page-register">
<div class="form-wrap">
<form action="/register" method="POST">
<div class="form-input">
<label for="username">Username:</label>
<input type="text" name="username" id="username" required />
</div>
<div class="form-input">
<label for="contact">Contact:</label>
<input type="text" name="contact" id="contact" required />
</div>
<div class="form-input">
<label for="password">Password:</label>
<input type="password" name="password" placeholder="optional" id="password"/>
</div>
<div class="btn-wrap">
<button class="btn">Submit</button>
</div>
</form>
</div>
</main>
</body>
</html>

View File

@@ -1,20 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Submit</title>
<link rel="stylesheet" href="/static/style.css">
</head>
<body>
<main>
<form action="/submit" method="POST">
<label for="forminput1">forminput1</label>
<input type="text" name="forminput1" id="forminput1" required>
<label for="forminput2">forminput2</label>
<input type="text" name="forminput2" id="forminput2" required>
<button> Submit </button>
</form>
</main>
</body>
</html>