Compare commits

...

4 Commits

Author SHA1 Message Date
ducoterra
a0e0afff1f switch to new ingress 2020-05-07 12:39:27 -04:00
ducoterra
8db4d5afed make the button red 2020-05-02 18:08:30 -04:00
ducoterra
f45e289ed8 proper mobile centering 2020-04-26 21:11:18 -04:00
ducoterra
43c48d5216 achievements tracking 2020-04-26 20:51:56 -04:00
16 changed files with 374 additions and 117 deletions

3
.gitignore vendored
View File

@@ -1,3 +1,4 @@
venv/ venv/
__pycache__/ __pycache__/
db/ db/
staticfiles/

6
.vscode/launch.json vendored
View File

@@ -12,7 +12,8 @@
"args": [ "args": [
"test", "test",
], ],
"django": true "django": true,
"preLaunchTask": "Migrate"
}, },
{ {
"name": "Run Server", "name": "Run Server",
@@ -23,7 +24,8 @@
"runserver", "runserver",
"--noreload" "--noreload"
], ],
"django": true "django": true,
"preLaunchTask": "Migrate"
} }
] ]
} }

24
.vscode/tasks.json vendored Normal file
View File

@@ -0,0 +1,24 @@
{
"version": "2.0.0",
"tasks": [
{
"label": "Collect Static",
"command": "venv/bin/python manage.py collectstatic --no-input",
"type": "shell",
"presentation": {
"reveal": "always"
},
"group": "build"
},
{
"dependsOn": "Collect Static",
"label": "Migrate",
"command": "venv/bin/python manage.py migrate",
"type": "shell",
"presentation": {
"reveal": "always"
},
"group": "build"
}
]
}

View File

@@ -24,6 +24,7 @@ SECRET_KEY = os.getenv("SECRET_KEY")
# SECURITY WARNING: don't run with debug turned on in production! # SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True if os.getenv("DEBUG") == "True" else False DEBUG = True if os.getenv("DEBUG") == "True" else False
LOGGING_CONFIG = None
ALLOWED_HOSTS = os.getenv("ALLOWED_HOSTS", "localhost").split(",") ALLOWED_HOSTS = os.getenv("ALLOWED_HOSTS", "localhost").split(",")

View File

@@ -1,46 +1,31 @@
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
annotations:
ingress.kubernetes.io/ssl-redirect: "true"
name: $DEPLOY
spec:
tls:
- hosts:
- $DEPLOY.ducoterra.net
secretName: letsencrypt
rules:
- host: $DEPLOY.ducoterra.net
http:
paths:
- backend:
serviceName: $DEPLOY
servicePort: 8000
---
apiVersion: traefik.containo.us/v1alpha1 apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute kind: IngressRoute
metadata: metadata:
name: $DEPLOY-external-tls name: $DEPLOY-internal-tls
annotations:
kubernetes.io/ingress.class: traefik-internal
spec: spec:
entryPoints: entryPoints:
- websecure - websecure
tls: tls:
secretName: letsencrypt certResolver: myresolver
domains:
- main: "*.ducoterra.net"
routes: routes:
- match: Host(`$DEPLOY.ducoterra.net`) - match: Host(`$DEPLOY.ducoterra.net`)
kind: Rule kind: Rule
services: services:
- name: $DEPLOY - name: $DEPLOY
port: 8000 port: 80
--- ---
apiVersion: traefik.containo.us/v1alpha1 apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute kind: IngressRoute
metadata: metadata:
name: $DEPLOY-external-web name: $DEPLOY-internal-web
annotations:
kubernetes.io/ingress.class: traefik-internal
spec: spec:
entryPoints: entryPoints:
- web - web
@@ -49,6 +34,46 @@ spec:
kind: Rule kind: Rule
services: services:
- name: $DEPLOY - name: $DEPLOY
port: 8000 port: 80
middlewares:
- name: httpsredirect
---
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
name: $DEPLOY-external-tls
annotations:
kubernetes.io/ingress.class: traefik-external
spec:
entryPoints:
- websecure
tls:
certResolver: myresolver
routes:
- match: Host(`$DEPLOY.ducoterra.net`)
kind: Rule
services:
- name: $DEPLOY
port: 80
---
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
name: $DEPLOY-external-web
annotations:
kubernetes.io/ingress.class: traefik-external
spec:
entryPoints:
- web
routes:
- match: Host(`$DEPLOY.ducoterra.net`)
kind: Rule
services:
- name: $DEPLOY
port: 80
middlewares: middlewares:
- name: httpsredirect - name: httpsredirect

View File

@@ -1,18 +1,39 @@
apiVersion: networking.k8s.io/v1beta1 apiVersion: traefik.containo.us/v1alpha1
kind: Ingress kind: IngressRoute
metadata: metadata:
name: $DEPLOY-internal-tls
annotations: annotations:
ingress.kubernetes.io/ssl-redirect: "true" kubernetes.io/ingress.class: traefik-internal
name: $DEPLOY
spec: spec:
entryPoints:
- websecure
tls: tls:
- hosts: certResolver: myresolver
- $DEPLOY.ducoterra.net domains:
secretName: letsencrypt - main: "*.ducoterra.net"
rules: routes:
- host: $DEPLOY.ducoterra.net - match: Host(`$DEPLOY.ducoterra.net`)
http: kind: Rule
paths: services:
- backend: - name: $DEPLOY
serviceName: $DEPLOY port: 80
servicePort: 8000
---
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
name: $DEPLOY-internal-web
annotations:
kubernetes.io/ingress.class: traefik-internal
spec:
entryPoints:
- web
routes:
- match: Host(`$DEPLOY.ducoterra.net`)
kind: Rule
services:
- name: $DEPLOY
port: 80
middlewares:
- name: httpsredirect

View File

@@ -1,11 +1,11 @@
.achievement { .achievement {
width: 100%; position: absolute;
height: 100%; height: 100%;
text-align: center; width: 100%;
display: flex;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
display: flex; z-index: -1;
position: absolute;
} }
.achievement-animate { .achievement-animate {
@@ -14,10 +14,22 @@
animation-fill-mode: forwards; animation-fill-mode: forwards;
} }
.achievment-column {
height: 100%;
overflow: auto;
text-align: center;
}
.achievement-text { .achievement-text {
font-size: 4rem; font-size: 4rem;
} }
@media only screen and (max-width: 768px) {
.achievment-column {
height: 50%;
}
}
@keyframes moveup { @keyframes moveup {
from {bottom: 0px;} from {bottom: 0px;}
to {bottom: 200px; color: white;} to {bottom: 200px; color: white;}

View File

@@ -1,18 +1,32 @@
html, body { html, body {
height: 100%; height: 100%;
width: 100%; width: 100%;
position: fixed;
overflow: hidden;
} }
.section, .container { .section, .container {
height: 100%; height: 100%;
} }
.container { .columns {
height: 100%;
}
.button-column {
display: flex; display: flex;
justify-content: center; justify-content: center;
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
text-align: center; text-align: center;
height: 100%;
}
@media only screen and (max-width: 768px) {
.button-column {
height: 50%;
}
}
.button-container {
position: absolute;
} }

View File

@@ -3,43 +3,21 @@ const button = document.getElementById("BUTTON");
const count = document.getElementById("COUNT"); const count = document.getElementById("COUNT");
const button_container = document.getElementById("button-container"); const button_container = document.getElementById("button-container");
const achievement = document.getElementById("achievement"); const achievement = document.getElementById("achievement");
const achievement_list = document.getElementById("achievement-list");
const achievements = { const achievement_column = document.getElementById("achievement-column");
1: "Clicked!",
2: "Clicked Twice!",
4: "2^2",
8: "2^3",
16: "2^4",
32: "2^5",
64: "2^6",
69: "Nice",
100: "one hundred",
128: "2^7",
200: "two hundred",
250: "quarter thousand",
256: "2^8",
420: "Blaze it",
500: "half thousand",
512: "2^9",
1000: "one thousand",
1024: "2^10",
2048: "2048!",
2500: "Keep going!",
4096: "2^11",
5000: "halfway to ten thousand"
}
function add_achievement(text) { function add_achievement(text) {
achievement.querySelector(".achievement-text").innerText = text; if (text != undefined) {
achievement.classList.remove("achievement-animate"); achievement.querySelector(".achievement-text").innerText = text;
void achievement.offsetWidth; achievement.classList.remove("achievement-animate");
achievement.classList.add("achievement-animate"); void achievement.offsetWidth;
} achievement.classList.add("achievement-animate");
function check_achievement(count) { var elem = document.createElement("div");
current_achievement = achievements[Number(count)] elem.innerText = text;
if (current_achievement != undefined) { achievement_list.appendChild(elem);
add_achievement(current_achievement)
achievement_column.scrollTo(0, achievement_list.scrollHeight);
} }
} }
@@ -59,7 +37,7 @@ button.addEventListener("click", event => {
}) })
.then((data) => { .then((data) => {
count.innerText = data.pressed; count.innerText = data.pressed;
check_achievement(data.pressed); add_achievement(data.achievement);
}).finally(() => { }).finally(() => {
button.disabled = false; button.disabled = false;
button.classList.remove("is-loading"); button.classList.remove("is-loading");

23
ui/static/ui/smooth.css Normal file
View File

@@ -0,0 +1,23 @@
html, body, .section, .container {
height: 100%;
}
.container {
overflow-y: hidden;
}
.element::-webkit-scrollbar { width: 0 !important }
.element { overflow: -moz-scrollbars-none; }
.element { -ms-overflow-style: none; }
.hide-overflow {
overflow: hidden;
}
.h-100 {
height: 100%;
}
.w-100 {
width: 100%;
}

39
ui/static/ui/smooth.js Normal file
View File

@@ -0,0 +1,39 @@
// temp0.scrollIntoView({behavior: "smooth"});
const scroll0 = document.getElementById("scroll-0");
const scroll1 = document.getElementById("scroll-1");
const scroll2 = document.getElementById("scroll-2");
const scroll3 = document.getElementById("scroll-3");
window.addEventListener("scroll", function(event) {
window.addEventListener("mouseup", event => {
console.log("hello");
});
event.preventDefault();
document.querySelector("html").classList.add("hide-overflow");
if (scroll0.dataset.isScrolling != "true") {
scroll0.dataset.isScrolling = 'true';
if (scroll1.dataset.seen != "true") {
scroll1.dataset.seen = "true";
scroll1.scrollIntoView({behavior: "smooth"});
}
else if (scroll2.dataset.seen != "true") {
scroll2.dataset.seen = "true";
scroll2.scrollIntoView({behavior: "smooth"});
}
else if (scroll3.dataset.seen != "true") {
scroll3.dataset.seen = "true";
scroll3.scrollIntoView({behavior: "smooth"});
}
else {
scroll1.dataset.seen = "false";
scroll2.dataset.seen = "false";
scroll3.dataset.seen = "false";
scroll0.scrollIntoView({behavior: "smooth"});
}
setTimeout(() => {
scroll0.dataset.isScrolling = false;
document.querySelector("html").classList.remove("hide-overflow");
}, 1000)
}
});

View File

@@ -14,21 +14,33 @@
{% block body %} {% block body %}
<section class="section"> <section class="section">
<div id = "button-container" class="container"> <div class="columns">
<div id = "achievement" class="achievement"> <div class="column">
</div>
<div class="button-column column">
<div> <div>
<div class="achievement-text"></div> <h1 class="title">
The Button
</h1>
<button class="button is-danger" id="BUTTON">Press</button>
</div>
<div><br></div>
<div>
<h1 class="title" id="COUNT">{{ pressed }}</h1>
</div>
<div id="achievement" class="achievement">
<div>
<div class="achievement-text"></div>
</div>
</div> </div>
</div> </div>
<div> <div class="column achievment-column" id = "achievement-column">
<h1 class="title"> <div class="achievements-list" id="achievement-list">
The Button <h1 class="title">Achievements</h1>
</h1> {% for key,value in achievement.items %}
<button class="button is-primary" id="BUTTON">Press</button> <div>{{ value }}</div>
</div> {% endfor %}
<div><br></div> </div>
<div>
<h1 class="title" id="COUNT">{{ pressed }}</h1>
</div> </div>
</div> </div>
</section> </section>

View File

@@ -0,0 +1,31 @@
{% extends 'ui/base.html' %}
{% load static %}
{% block css %}
<link rel="stylesheet" href="{% static 'ui/smooth.css' %}">
{% endblock %}
{% block js %}
<script src="{% static 'ui/smooth.js' %}"></script>
{% endblock %}
{% block body %}
<div class="container">
<div id="scroll-0" class="h-100 w-100">
<h1 class="title">Hello There</h1>
</div>
<div id="scroll-1" class="h-100 w-100">
<h1 class="title">I'm an Apple Ad</h1>
</div>
<div id="scroll-2" class="h-100 w-100">
<h1 class="title">See me scroll</h1>
</div>
<div id="scroll-3" class="h-100 w-100">
<h1 class="title">You owe me $3,000 dollars for this</h1>
</div>
</div>
{% endblock %}

View File

@@ -1,5 +1,5 @@
from django.contrib.auth.models import AnonymousUser, User from django.contrib.auth.models import AnonymousUser, User
from django.test import RequestFactory, TestCase from django.test import RequestFactory, TestCase, Client
from .views import button from .views import button
@@ -11,23 +11,31 @@ class SimpleTest(TestCase):
username='testuser', email='test@test.test', password='testpass') username='testuser', email='test@test.test', password='testpass')
def test_button(self): def test_button(self):
# Create an instance of a GET request. # Test initial load
request = self.factory.get('/snippets') c = Client()
request.user = self.user response = c.get('/button')
request.session = self.client.session self.assertEqual(response.status_code, 301)
response = button(request) response = c.get('/button/')
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
self.assertEqual(response.context.get("achievement"), {})
request = self.factory.post(
'/button',
data={},
content_type='application/json'
)
request.session = self.client.session
response = button(request)
self.assertEqual(response.status_code, 200)
self.assertEqual(request.session.get('pressed'), 1)
response = button(request) # Test first achievement
response = c.post('/button/', {})
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
self.assertEqual(request.session.get('pressed'), 2) self.assertEqual(response.json().get("pressed"), 1)
self.assertEqual(response.json().get("achievement"), "Clicked!")
self.assertEqual(c.session.get('pressed'), 1)
# Test second achievement
response = c.post('/button/', {})
self.assertEqual(response.status_code, 200)
self.assertEqual(response.json().get("pressed"), 2)
self.assertEqual(response.json().get("achievement"), "Clicked Twice!")
self.assertEqual(c.session.get('pressed'), 2)
# Test no achievement
response = c.post('/button/', {})
self.assertEqual(response.status_code, 200)
self.assertEqual(response.json().get("pressed"), 3)
self.assertEqual(response.json().get("achievement"), None)
self.assertEqual(c.session.get('pressed'), 3)

View File

@@ -3,4 +3,5 @@ from . import views
urlpatterns = [ urlpatterns = [
path('button/', views.button, name = 'button'), path('button/', views.button, name = 'button'),
path('smooth/', views.smooth, name = 'smooth'),
] ]

View File

@@ -1,8 +1,61 @@
from django.shortcuts import render from django.shortcuts import render
from django.http import JsonResponse from django.http import JsonResponse
achievements = {
1: "Clicked!",
2: "Clicked Twice!",
4: "2^2",
8: "2^3",
16: "2^4",
24: "I'm that old",
32: "2^5",
64: "2^6",
69: "Nice",
100: "one hundred",
128: "2^7",
200: "two hundred",
250: "quarter thousand",
256: "2^8",
300: "three hundred",
400: "four hundred",
420: "Blaze it",
500: "half thousand",
512: "2^9",
600: "six hundred",
700: "seven hundred",
800: "eight hundred",
900: "nine hundred",
1000: "full thousand",
1024: "2^10",
1776: "America",
1914: "Some War here",
1938: "Some more war here",
1950: "Lots of war in here",
2000: "Computers die",
2008: "Houses die",
2019: "People die",
2048: "2048!",
2500: "Keep going!",
3000: "three thousand",
4000: "four thousand",
4096: "2^11",
5000: "halfway to ten thousand",
10001: "ten thousand one",
100000: "one hundred thousand",
1000000: "one million?",
10000000: "ten millions???",
100000000: "one hundo billion",
1000000000: "JK this is actually a billion though",
10000000000: "I'm not going to create another achievement",
100000000000: "one hundred billion",
1000000000000: "It's physically impossible to click this high"
}
def button(request): def button(request):
PRESSED = 'pressed' PRESSED = 'pressed'
ACHIEVE = 'achievement'
try: try:
request.session[PRESSED] request.session[PRESSED]
except KeyError: except KeyError:
@@ -10,5 +63,17 @@ def button(request):
if request.method == "POST": if request.method == "POST":
request.session[PRESSED] += 1 request.session[PRESSED] += 1
return JsonResponse({PRESSED: request.session[PRESSED]}) response = {
return render(request, "ui/button.html", {PRESSED: request.session[PRESSED]}) PRESSED: request.session[PRESSED],
ACHIEVE: achievements.get(request.session[PRESSED])
}
return JsonResponse(response)
pressed = request.session[PRESSED]
response = {PRESSED: pressed}
achieved = {k:v for k,v in achievements.items() if k <= pressed}
response.update({ACHIEVE: achieved})
return render(request, "ui/button.html", response)
def smooth(request):
return render(request, "ui/smooth.html")