Skip to content

Commit 4396bf1

Browse files
committed
Create simple Django project to learn testing
1 parent cb0d617 commit 4396bf1

30 files changed

+985
-0
lines changed

Pipfile

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
[[source]]
2+
url = "https://pypi.org/simple"
3+
verify_ssl = true
4+
name = "pypi"
5+
6+
[packages]
7+
django = "==2.1.2"
8+
pytz = "==2018.4"
9+
10+
[dev-packages]
11+
12+
[requires]
13+
python_version = "3.8"

Pipfile.lock

Lines changed: 37 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

budget/admin.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
from django.contrib import admin
2+
from .models import Project, Expense, Category
3+
4+
admin.site.register(Project)
5+
admin.site.register(Expense)
6+
admin.site.register(Category)

budget/apps.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
from django.apps import AppConfig
2+
3+
4+
class BudgetConfig(AppConfig):
5+
name = 'budget'

budget/forms.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
from django import forms
2+
3+
4+
class ExpenseForm(forms.Form):
5+
title = forms.CharField()
6+
amount = forms.IntegerField()
7+
category = forms.CharField()

budget/migrations/0001_initial.py

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
# Generated by Django 2.0.4 on 2018-04-19 12:40
2+
3+
from django.db import migrations, models
4+
import django.db.models.deletion
5+
6+
7+
class Migration(migrations.Migration):
8+
9+
initial = True
10+
11+
dependencies = [
12+
]
13+
14+
operations = [
15+
migrations.CreateModel(
16+
name='Category',
17+
fields=[
18+
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
19+
('name', models.CharField(max_length=50)),
20+
],
21+
),
22+
migrations.CreateModel(
23+
name='Expense',
24+
fields=[
25+
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
26+
('title', models.CharField(max_length=100)),
27+
('amount', models.DecimalField(decimal_places=2, max_digits=8)),
28+
('category', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='budget.Category')),
29+
],
30+
),
31+
migrations.CreateModel(
32+
name='Project',
33+
fields=[
34+
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
35+
('name', models.CharField(max_length=100)),
36+
('slug', models.SlugField(max_length=100, unique=True)),
37+
('budget', models.IntegerField()),
38+
],
39+
),
40+
migrations.AddField(
41+
model_name='expense',
42+
name='project',
43+
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='budget.Project'),
44+
),
45+
migrations.AddField(
46+
model_name='category',
47+
name='project',
48+
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='budget.Project'),
49+
),
50+
]
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# Generated by Django 2.0.4 on 2018-04-19 12:46
2+
3+
from django.db import migrations
4+
5+
6+
class Migration(migrations.Migration):
7+
8+
dependencies = [
9+
('budget', '0001_initial'),
10+
]
11+
12+
operations = [
13+
migrations.RemoveField(
14+
model_name='category',
15+
name='project',
16+
),
17+
migrations.RemoveField(
18+
model_name='expense',
19+
name='category',
20+
),
21+
migrations.RemoveField(
22+
model_name='expense',
23+
name='project',
24+
),
25+
migrations.DeleteModel(
26+
name='Category',
27+
),
28+
migrations.DeleteModel(
29+
name='Expense',
30+
),
31+
migrations.DeleteModel(
32+
name='Project',
33+
),
34+
]
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
# Generated by Django 2.0.4 on 2018-04-19 12:51
2+
3+
from django.db import migrations, models
4+
import django.db.models.deletion
5+
6+
7+
class Migration(migrations.Migration):
8+
9+
initial = True
10+
11+
dependencies = [
12+
('budget', '0002_auto_20180419_1446'),
13+
]
14+
15+
operations = [
16+
migrations.CreateModel(
17+
name='Category',
18+
fields=[
19+
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
20+
('name', models.CharField(max_length=50)),
21+
],
22+
),
23+
migrations.CreateModel(
24+
name='Expense',
25+
fields=[
26+
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
27+
('title', models.CharField(max_length=100)),
28+
('amount', models.DecimalField(decimal_places=2, max_digits=8)),
29+
('category', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='budget.Category')),
30+
],
31+
),
32+
migrations.CreateModel(
33+
name='Project',
34+
fields=[
35+
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
36+
('name', models.CharField(max_length=100)),
37+
('slug', models.SlugField(blank=True, max_length=100, unique=True)),
38+
('budget', models.IntegerField()),
39+
],
40+
),
41+
migrations.AddField(
42+
model_name='expense',
43+
name='project',
44+
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='budget.Project'),
45+
),
46+
migrations.AddField(
47+
model_name='category',
48+
name='project',
49+
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='budget.Project'),
50+
),
51+
]

budget/models.py

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
from django.db import models
2+
from django.utils.text import slugify
3+
4+
5+
class Project(models.Model):
6+
name = models.CharField(max_length=100)
7+
slug = models.SlugField(max_length=100, unique=True, blank=True)
8+
budget = models.IntegerField()
9+
10+
def save(self, *args, **kwargs):
11+
self.slug = slugify(self.name)
12+
super(Project, self).save(*args, **kwargs)
13+
14+
@property
15+
def budget_left(self):
16+
expense_list = Expense.objects.filter(project=self)
17+
total_expense_amount = 0
18+
for expense in expense_list:
19+
total_expense_amount += expense.amount
20+
21+
# temporary solution, because the form currently only allows integer amounts
22+
total_expense_amount = int(total_expense_amount)
23+
24+
return self.budget - total_expense_amount
25+
26+
@property
27+
def total_transactions(self):
28+
expense_list = Expense.objects.filter(project=self)
29+
return len(expense_list)
30+
31+
def get_absolute_url(self):
32+
return '/' + self.slug
33+
34+
35+
class Category(models.Model):
36+
project = models.ForeignKey(Project, on_delete=models.CASCADE)
37+
name = models.CharField(max_length=50)
38+
39+
40+
class Expense(models.Model):
41+
project = models.ForeignKey(Project, on_delete=models.CASCADE, related_name='expenses')
42+
title = models.CharField(max_length=100)
43+
amount = models.DecimalField(max_digits=8, decimal_places=2)
44+
category = models.ForeignKey(Category, on_delete=models.CASCADE)
45+
46+
class Meta:
47+
ordering = ('-amount',)

budget/static/css/styles.css

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
@import url('https://fonts.googleapis.com/css?family=Montserrat');
2+
3+
/* General */
4+
html {
5+
font-family: 'Montserrat';
6+
background-color: #E0E0E0;
7+
}
8+
h1, h6 {
9+
font-weight: bold;
10+
}
11+
.bold {
12+
font-weight: bold;
13+
}
14+
.row {
15+
margin: 0!important;
16+
}
17+
.row .col {
18+
padding-left: 0!important;
19+
}
20+
21+
/* project list (no projects) */
22+
.noproject-wrapper {
23+
margin-top: 250px;
24+
padding: 80px 0px;
25+
border: 6px solid #9e9e9e;
26+
border-style: dashed;
27+
}
28+
.noproject-wrapper h3 {
29+
padding: 10px;
30+
margin-bottom: 50px;
31+
}
32+
33+
/* project add view */
34+
form ul li.category {
35+
display: inline-block;
36+
background-color: white;
37+
padding: 5px 10px;
38+
margin-right: 5px;
39+
}
40+
41+
/* project detail statistics */
42+
.section-stats h1 {
43+
font-size: 4vw;
44+
margin: 15px 0px;
45+
}
46+
@media(max-width: 992px){
47+
.section-stats h1 {
48+
font-size: calc(16px + 6vw);
49+
}
50+
}
51+
52+
/* project detail expenses */
53+
.section-expenses button {
54+
padding: 0px 40px;
55+
font-weight: bold;
56+
text-shadow: 0px 3px 4px rgba(0, 0, 0, .25);
57+
background-color: #6FCF97;
58+
}
59+
.section-expenses .expense {
60+
font-size: 16px;
61+
border-radius: 0;
62+
margin: 0;
63+
}
64+
.section-expenses .expense .category {
65+
font-size: 13px;
66+
}
67+
.section-expenses li:nth-child(even) .expense {
68+
background-color: #F2F2F2;
69+
}
70+
.section-expenses .close-icon:hover {
71+
cursor: pointer;
72+
}

budget/static/js/script.js

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
2+
(function(){
3+
4+
document.querySelector('#categoryInput').addEventListener('keydown', function(e){
5+
if (e.keyCode != 13){
6+
return;
7+
}
8+
9+
e.preventDefault()
10+
11+
var categoryName = this.value
12+
this.value = ''
13+
addNewCategory(categoryName)
14+
updateCategoriesString()
15+
})
16+
17+
function addNewCategory(name){
18+
19+
document.querySelector('#categoriesContainer').insertAdjacentHTML('beforeend',
20+
`<li class="category">
21+
<span class="name">${name}</span>
22+
<span onclick="removeCategory(this)" class="btnRemove bold">X</span>
23+
</li>`)
24+
}
25+
26+
})()
27+
28+
function fetchCategoryArray(){
29+
var categories = []
30+
31+
document.querySelectorAll('.category').forEach(function(e){
32+
name = e.querySelector('.name').innerHTML
33+
if (name == '') return
34+
35+
categories.push(name)
36+
})
37+
38+
return categories
39+
}
40+
41+
function updateCategoriesString(){
42+
categories = fetchCategoryArray()
43+
document.querySelector('input[name="categoriesString"]').value = categories.join(',')
44+
}
45+
46+
function removeCategory(e){
47+
e.parentElement.remove()
48+
updateCategoriesString()
49+
}

0 commit comments

Comments
 (0)