first commit

main
Michael Murtaugh 1 week ago
commit 646e3d51a3

4
.gitignore vendored

@ -0,0 +1,4 @@
db/
**/__pycache__/
media/
venv/

@ -0,0 +1,27 @@
How does 2bitcharactergenerator actually work?
Let's make all the canvas elements visible...
The stylesheet sets "image-rendering: pixelated" on the canvas element, which seems to be supported on contemporary Firefox + Chrome. The elements have width and height attributes set to 16, while their height in CSS is set to 64px producing a (pixelated) scaling up. However the actual size in the canvas coordinates remains unscaled.
This makes caption generation in the canvas tricky.
https://developer.mozilla.org/en-US/docs/Games/Techniques/Crisp_pixel_art_look
diggin into the history of workadventure
https://web.archive.org/web/20210623060444/https://thecodingmachine.io/energy-consumption-web-game-engine
https://medium.com/@michaelwesthadley/modular-game-worlds-in-phaser-3-tilemaps-1-958fc7e6bbd6
https://docs.phaser.io/api-documentation/class/tilemaps-tilemap
FOCUS
* CALL as good self-contained page.
* POST -> LINK to "visit (rp)guestbook"
* ANIMATION GENERATOR... (for mail/masto/IG)
"I want to be"

@ -0,0 +1,5 @@
from django.contrib import admin
from .models import Creature
admin.site.register(Creature)

@ -0,0 +1,6 @@
from django.apps import AppConfig
class CallConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'call'

@ -0,0 +1,14 @@
from django.forms import ModelForm
from call.models import Creature
class CreatureForm(ModelForm):
class Meta:
model = Creature
fields = ["image", "caption", "message"]
class CreatureFormNoImage(ModelForm):
class Meta:
model = Creature
fields = ["caption", "message"]

@ -0,0 +1,25 @@
# Generated by Django 5.1.5 on 2025-01-21 07:45
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
]
operations = [
migrations.CreateModel(
name='Creature',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('image', models.ImageField(upload_to='creatures')),
('caption', models.CharField(max_length=255)),
('created', models.DateTimeField(auto_now_add=True, verbose_name='date created')),
('updated', models.DateTimeField(auto_now=True, verbose_name='date updated')),
('message', models.TextField(max_length=512)),
],
),
]

@ -0,0 +1,18 @@
# Generated by Django 5.1.5 on 2025-01-21 07:51
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('call', '0001_initial'),
]
operations = [
migrations.AlterField(
model_name='creature',
name='message',
field=models.TextField(blank=True, max_length=512),
),
]

@ -0,0 +1,12 @@
from django.db import models
class Creature(models.Model):
image = models.ImageField(upload_to="creatures")
caption = models.CharField(max_length=255)
created = models.DateTimeField("date created", auto_now_add=True)
updated = models.DateTimeField("date updated", auto_now=True)
message = models.TextField(max_length=512, blank=True)
class Meta:
ordering = ["-created"]

@ -0,0 +1,395 @@
var PARTS_MAX = {
eyes: 12,
mouth: 8,
ears: 6,
torso: 5,
hair: 22,
};
var gParts = {}
var gColor = 0;
var gColorMode = '012';
var showCanvas = document.getElementById('c');
var outCanvas = document.getElementById('outc');
outCanvas.width = 16 * 6;
outCanvas.height = 16 * 1;
var ctx = showCanvas.getContext('2d');
var outCtx = outCanvas.getContext('2d');
var outCanvas_gbs_s = document.getElementById("outc_gbs_s")
outCanvas_gbs_s.width = 48
outCanvas_gbs_s.height = 16
var outCanvas_gbs_a = document.getElementById("outc_gbs_a")
outCanvas_gbs_a.width = 96
outCanvas_gbs_a.height = 16
var outCtx_gbs_s = outCanvas_gbs_s.getContext('2d');
var outCtx_gbs_a = outCanvas_gbs_a.getContext('2d');
var a = function(n) {
return n
.split(',')
.map(function(k){ return { x: parseInt(k), f: !!k.match('f')} });
}
var ANIMS = {
stand: a("0"),
rotate: a("0,2f,1,2"),
run_front: a("0,3,0f,3f"),
run_back: a("1,4,1f,4f"),
run_left: a("2,5"),
run_right: a("2f,5f"),
}
var ANIM_NAMES = Object.keys(ANIMS);
var currentAnimName = "stand";
var gTextures = { loaded: 0, requested: 0 };
loadAllTextures();
var __appstarted = false;
function loadTexture(key) {
// var tex = new Image();
var tex = document.querySelector("#all_imgs").querySelector('#img_'+key);
// tex.src = "./" + key + ".png?v=" + Math.random();
gTextures.requested++;
gTextures[key] = tex;
var tex_onload = function() {
gTextures.loaded++;
if(gTextures.loaded === gTextures.requested){
if(__appstarted){
updateOutCanvas();
} else {
startApp();
}
} else {
document.getElementById('loading').innerText =
"loading... " + gTextures.loaded + " / " + gTextures.requested;
}
}
if (tex.complete) {
window.setTimeout(tex_onload, 10);
} else {
tex.onload = tex_onload;
}
}
function loadAllTextures() {
loadTexture("hair");
loadTexture("base");
loadTexture("eyes");
loadTexture("mouth");
loadTexture("ears");
loadTexture("torso");
loadTexture("hands");
}
function startApp() {
__appstarted = true;
setupInput("eyes")
setupInput("mouth")
setupInput("ears")
setupInput("torso")
setupInput("hair")
setupColorInput();
setupColorMode();
updateOutCanvas();
updateAnimBtnText();
window.setInterval(animate, 16);
document.getElementById('loading').innerText = '';
document.getElementById('root').className = '';
}
function animate(){
ctx.clearRect(0, 0, 16, 16);
var t = Date.now ? Date.now() : +(new Date());
var nt = parseInt(t / 200)
var anim = ANIMS[currentAnimName];
var len = anim.length;
var findex = nt % len;
var frame = anim[findex];
ctx.save();
if(frame.f){
ctx.translate(16, 0);
ctx.scale(-1, 1);
}
ctx.drawImage(outCanvas, frame.x * 16, 0, 16, 16, 0, 0, 16, 16);
ctx.restore();
}
var animbtn = document.getElementById('animbtn');
function updateAnimBtnText(){
animbtn.innerText = currentAnimName.replace("_", " ");
}
animbtn.onclick = function() {
var index = ANIM_NAMES.indexOf(currentAnimName);
currentAnimName = ANIM_NAMES[index + 1] || ANIM_NAMES[0];
updateAnimBtnText();
}
showCanvas.onclick = animbtn.onclick;
// var randomizebtn = document.getElementById('randomizebtn');
// randomizebtn.onclick = function() {
// var inputs = document.getElementById('inputs').querySelectorAll('input');
// for(var i=0; i<inputs.length; i++){
// var input = inputs[i];
// var key = input.id.replace("Input", "");
// var max = PARTS_MAX[key];
// var randval = Math.floor(Math.random() * (max + 1));
// input.value = randval;
// gParts[key] = randval;
// }
// updateOutCanvas();
// }
// 13 * 9 * 7 * 6 * 23 = 113'022
var COLORS = [
["#000000", "#787878", "#e8e8e8", "#ffffff", "Basic"],
["#071821", "#86c06c", "#e0f8cf", "#65ff00", "GBS"],
["#7c3f58", "#eb6b6f", "#f9a875", "#fff6d3", "ICE CREAM GB PALETTE by Kerrie Lake"],
["#2d1b00", "#1e606e", "#5ab9a8", "#c4f0c2", "MIST GB PALETTE by Kerrie Lake"],
["#2c2137", "#764462", "#edb4a1", "#a96868", "RUSTIC GB PALETTE by Kerrie Lake"],
["#20283d", "#426e5d", "#e5b083", "#fbf7f3", "EN4 PALETTE by ENDESGA"],
["#4c1c2d", "#d23c4e", "#5fb1f5", "#eaf5fa", "GRAND DAD 4 PALETTE by Starlane"],
[ "#181010", "#84739c", "#f7b58c", "#ffefff", "POKEMON (SGB) PALETTE" ],
[ "#2b2b26", "#706b66", "#a89f94", "#e0dbcd", "GRAFXKID GAMEBOY POCKET (GRAY) PALETTE by GrafxKid" ],
[ "#000000", "#ff55ff", "#55ffff", "#ffffff", "CGA PALETTE 1 (HIGH) PALETTE" ],
[ "#243137", "#3f503f", "#768448", "#acb56b", "NINTENDO GAMEBOY (ARNE) PALETTE by Arne" ],
[ "#260016", "#ed008c", "#00bff3", "#daf3ec", "JB4 PALETTE by Haretro for his game Jet Boy" ],
[ "#253b46", "#18865f", "#61d162", "#ebe7ad", "SWEET GUARANÁ PALETTE by MadPezkoh" ],
[ "#4c625a", "#7b9278", "#abc396", "#dbf4b4", "GRAFXKID GAMEBOY POCKET (GREEN) PALETTE by GrafxKid" ],
];
function drawPart(key, forceY, forceFrame){
var y = gParts[key] == null ? forceY : gParts[key]; // 0's falsy
if(y == null){ return }
var w = 16 * 6;
var h = 16;
if(forceFrame || forceFrame === 0){
outCtx.drawImage(gTextures[key], forceFrame*16, y*16, 16, 16, forceFrame*16, 0, 16, 16);
} else {
outCtx.drawImage(gTextures[key], 0, y*16, w, h, 0, 0, w, h);
}
}
function GBS_recolor_fn()
{
return {
dark: parseColorStr("#071821"),
mid: parseColorStr("#86c06c"),
light: parseColorStr("#e0f8cf"),
bg: parseColorStr("#65ff00"),
}
}
function GBS_recolor(ctx, w)
{
var imageData = ctx.getImageData(0, 0, w || 96, 16);
recolor(imageData.data, GBS_recolor_fn);
ctx.putImageData(imageData,0,0);
}
function ff(ctx, src, f, sx, sy, dx)
{
f && ctx.save();
f && ctx.scale(-1, 1);
ctx.drawImage(src, 16*sx, sy*16, 16, 16, dx*16, 0, 16, 16);
f && ctx.restore();
}
var FLIP = true;
function updateGBS()
{
ff(outCtx_gbs_s, outCanvas, null, 0, 0, 0);
ff(outCtx_gbs_s, outCanvas, null, 1, 0, 1);
ff(outCtx_gbs_s, outCanvas, FLIP, 2, 0, -3);
GBS_recolor(outCtx_gbs_s, 48);
updateExportLink("export_gbs_static", outCanvas_gbs_s, "_gbs");
ff(outCtx_gbs_a, outCanvas, null, 3, 0, 0)
ff(outCtx_gbs_a, outCanvas, FLIP, 3, 0, -2)
ff(outCtx_gbs_a, outCanvas, null, 4, 0, 3)
ff(outCtx_gbs_a, outCanvas, FLIP, 4, 0, -3)
ff(outCtx_gbs_a, outCanvas, FLIP, 2, 0, -5)
ff(outCtx_gbs_a, outCanvas, FLIP, 5, 0, -6)
GBS_recolor(outCtx_gbs_a, 96);
updateExportLink("export_gbs_animated", outCanvas_gbs_a, "_gbs_anim");
}
function parseColorStr(str){
return [
parseInt(str.substr(1, 2), 16),
parseInt(str.substr(3, 2), 16),
parseInt(str.substr(5, 2), 16),
255
];
}
function getColors()
{
function C(n){
return [
parseInt(COLORS[gColor][n].substr(1, 2), 16),
parseInt(COLORS[gColor][n].substr(3, 2), 16),
parseInt(COLORS[gColor][n].substr(5, 2), 16)
];
}
var mode = gColorMode.split('').map(function(n){ return parseInt(n) });
var dark = C(mode[0]);
var mid = C(mode[1]);
var light = C(mode[2]);
var bg = C(3);
return { dark:dark, mid:mid, light:light, bg:bg };
}
function recolor(data, getColors)
{
var tmp = getColors();
var put1 = function(i, r, g, b, a) {
data[i+0] = r; data[i+1] = g; data[i+2] = b; data[i+3] = a;
}
for (var i=0; i<data.length; i+=4) {
var r = data[i];
if(r<50){
put1(i, tmp.dark[0], tmp.dark[1], tmp.dark[2], 255);
} else if(r<180){
put1(i, tmp.mid[0], tmp.mid[1], tmp.mid[2], 255);
} else if(r<245){
put1(i, tmp.light[0], tmp.light[1], tmp.light[2], 255);
} else {
put1(i, tmp.bg[0], tmp.bg[1], tmp.bg[2], tmp.bg[3]);
}
}
}
function updateExportLink(id, canvas, suffix)
{
// it's always a small file so this should be good enough:
var str = Object.keys(gParts).sort().map(function(k){
return ("00"+gParts[k]).slice(-2);
}).join('')
var exportlink = document.getElementById(id)
exportlink.href = canvas.toDataURL("image/png");
exportlink.download = str + suffix + '.png';
}
function updateOutCanvas() {
outCtx.clearRect(0, 0, outCanvas.width, outCanvas.height);
outCtx.drawImage(gTextures['base'], 0, 0); // base
drawPart("hair");
drawPart("torso");
drawPart("ears");
drawPart("mouth");
drawPart("eyes");
drawPart("hands", 0);
// back of the hair
drawPart("hair", null, 4);
drawPart("hair", null, 1);
updateGBS()
var imageData = outCtx.getImageData(0, 0, outCanvas.width, outCanvas.height);
var data = imageData.data
recolor(data, getColors);
outCtx.putImageData(imageData,0,0);
updateExportLink("exportlink", outCanvas, "_" + gColor + gColorMode)
// // it's always a small file so this should be good enough:
// var str = Object.keys(gParts).sort().map(function(k){
// return ("00"+gParts[k]).slice(-2);
// }).join('') + "_" + gColor + gColorMode;
// var exportlink = document.getElementById("exportlink")
// exportlink.href = outCanvas.toDataURL("image/png");
// exportlink.download = str + '.png';
// document.getElementById('serialized_input').value = s;
}
function prefixInputWithASpan(input, text){
var span = document.createElement("span");
span.innerText = text;
input.parentElement.prepend(span);
}
function setupInput(key){
var input = document.getElementById(key + "Input");
prefixInputWithASpan(input, key)
input.setAttribute('min', 0);
input.setAttribute('max', PARTS_MAX[key]);
input.value = parseInt(Math.random() * (PARTS_MAX[key] + 1));
var inputUpdate = function(e){
gParts[key] = parseInt(e.target.value);
console.log("INPUT UPDATE", e.target.value, gParts);
updateOutCanvas();
}
inputUpdate({ target: input });
input.onchange = inputUpdate;
input.oninput = inputUpdate;
};
function setupColorInput() {
var key = 'color';
var input = document.getElementById(key + "Input");
prefixInputWithASpan(input, 'colors')
input.setAttribute('min', 0);
input.setAttribute('max', COLORS.length - 1);
PARTS_MAX['color'] = COLORS.length;
input.value = 0;
var inputUpdate = function(e){
gColor = parseInt(e.target.value);
console.log("COLOR UPDATE", e.target.value, gColor);
document.getElementById('colorinfo').innerText = COLORS[gColor][4];
updateOutCanvas();
}
inputUpdate({ target: input });
input.onchange = inputUpdate;
input.oninput = inputUpdate;
}
function setupColorMode() {
var btn = document.getElementById("colormode");
btn.onclick = function() {
if(gColorMode == '012'){ gColorMode = '023' }
else if(gColorMode == '023'){ gColorMode = '013' }
else if(gColorMode == '013'){ gColorMode = '123' }
else { gColorMode = '012' }
btn.innerText = gColorMode;
updateOutCanvas();
}
}

@ -0,0 +1,3 @@
terms.json: terms.yaml
python3 update_terms_yaml_to_json.py $< $@

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 308 B

@ -0,0 +1,46 @@
.creature canvas {
width: 64px;
height: 64px;
image-rendering: optimizeSpeed;
image-rendering: -moz-crisp-edges;
image-rendering: -webkit-optimize-contrast;
image-rendering: -o-crisp-edges;
image-rendering: pixelated;
-ms-interpolation-mode: nearest-neighbor;
border: none;
}
div.creature {
display: inline-block;
width: fit-content;
text-align: center;
position: relative;
margin: 10em;
}
div.centerbottom {
position: absolute;
left: 50%;
bottom: 0;
}
div.caption {
/* border: 1px solid black; */
position: absolute;
left: -120px;
width: 240px;
}
div.caption .corner {
width: 11px;
height: 11px;
position: absolute;
top: -9px;
left: 49%;
background-image: url("corner.png");
}
div.caption div.captiontext {
/* height: 4em; */
width: 100%;
border: 1px solid black;
/* top | right | bottom | left */
padding: 0 0.3em 0.3em 0.3em;
}

@ -0,0 +1,81 @@
var a = function(n) {
return n
.split(',')
.map(function(k){ return { x: parseInt(k), f: !!k.match('f')} });
}
var ANIMS = {
stand: a("0"),
rotate: a("0,2f,1,2"),
run_front: a("0,3,0f,3f"),
run_back: a("1,4,1f,4f"),
run_left: a("2,5"),
run_right: a("2f,5f"),
}
class Creature {
constructor(img) {
this.img = img;
if (img.complete) {
this.init();
} else {
img.addEventListener("load", this.init.bind(this));
}
}
init () {
this.div = document.createElement("div");
this.div.classList.add("creature");
this.canvas = document.createElement("canvas");
this.img.parentNode.replaceChild(this.div, this.img);
this.div.appendChild(this.canvas);
this.ctx = this.canvas.getContext("2d");
this.canvas.width = 16;
this.canvas.height = 16;
this.ctx.drawImage(this.img, 0, 0);
// this.canvas.title = this.img.title;
this.centerbottom = document.createElement("div");
this.centerbottom.classList.add("centerbottom");
this.caption_div = document.createElement("div");
this.caption_corner = document.createElement("div");
this.caption = document.createElement("div");
this.caption_div.classList.add("caption");
this.caption_corner.classList.add("corner");
this.caption.classList.add("captiontext");
this.caption_div.appendChild(this.caption_corner);
this.caption_div.appendChild(this.caption);
this.div.appendChild(this.centerbottom);
this.centerbottom.appendChild(this.caption_div);
this.caption.textContent = this.img.title;
this.currentAnimName = "stand";
this.canvas.addEventListener("click", this.click.bind(this));
}
animate(){
this.ctx.clearRect(0, 0, 16, 16);
var t = Date.now ? Date.now() : +(new Date());
var nt = parseInt(t / 200)
var anim = ANIMS[this.currentAnimName];
var len = anim.length;
var findex = nt % len;
var frame = anim[findex];
const ctx = this.ctx;
ctx.save();
if(frame.f){
this.ctx.translate(16, 0);
this.ctx.scale(-1, 1);
}
ctx.drawImage(this.img, frame.x * 16, 0, 16, 16, 0, 0, 16, 16);
ctx.restore();
}
click () {
console.log("click", this);
this.currentAnimName = "run_front";
window.setInterval(this.animate.bind(this), 16);
}
}
document.querySelectorAll("img.creature").forEach ( img => {
new Creature(img);
})

@ -0,0 +1,83 @@
body, html { background: #fff; text-align: center; }
* { box-sizing: border-box; font-family: monospace; font-size: 14px; }
h1 { font-size: 28px; margin: 0 0 10px 0; padding: 0; }
#root {
width: 460px;
/* white-space: nowrap; */
margin: 0 auto;
}
canvas {
image-rendering: optimizeSpeed;
image-rendering: -moz-crisp-edges;
image-rendering: -webkit-optimize-contrast;
image-rendering: -o-crisp-edges;
image-rendering: pixelated;
-ms-interpolation-mode: nearest-neighbor;
border: 1px solid black;
}
#c { width: 144px; height: 144px; padding: 8px; border: none; }
#outc,
#outc_gbs_s,
#outc_gbs_a {
height: 64px;
padding: 8px;
display: none;
}
label { display: block; }
label > input { width: 240px; }
label > span { display: inline-block; vertical-align: middle; width: 55px; }
input[type=range]{ vertical-align: middle; }
#inputs { display: inline-block; vertical-align: top; text-align: left; margin-top: 1em;}
.btn, button { background: #fff; text-align: center; min-width: 144px; display: inline-block; padding: 16px 32px; border: 1px solid black; color: black; text-decoration: none; font-size: 1em; margin: 0 0 10px 0; }
.btn:hover, button:hover { background: #eee }
.btn:active, button:active{ background: #ccc }
.sm {
min-width: 40px;
padding: 10px 16px;
}
.rootloading { display: none }
div.colormode { max-width: 240px; white-space: nowrap }
#caption {
font-family: monospace;
font-size: 18px;
}
div.caption {
/* border: 3px solid black; */
position: relative;
width: fit-content;
margin-left: auto;
margin-right: auto;
}
div.caption .corner {
width: 11px;
height: 11px;
position: absolute;
top: -9px;
left: 49%;
background-image: url("corner.png");
}
div.caption textarea {
width: 320px;
height: 4em;
}
#colofon {
text-align: left;
}
#info, #info p {
font-family: serif;
}
#info {
padding: 0 1em 0 1em;
border: 1px solid black;
text-align: left;
}

@ -0,0 +1,104 @@
/* https://codepen.io/josfabre/pen/EBMWwW */
body {
background-color: #CCC;
padding: 60px;
}
.bubble {
position: relative;
display: inline-block;
margin: 20px;
text-align: center;
font-family: "Press Start 2P", cursive;
font-size: 16px;
line-height: 1.3em;
background-color: #fff;
color: #000;
padding: 4px;
box-shadow: 0 -4px #fff, 0 -8px #000, 4px 0 #fff, 4px -4px #000, 8px 0 #000, 0 4px #fff, 0 8px #000, -4px 0 #fff, -4px 4px #000, -8px 0 #000, -4px -4px #000, 4px 4px #000;
box-sizing: border-box;
width: 200px;
}
.bubble::after {
content: "";
display: block;
position: absolute;
box-sizing: border-box;
}
.bubble.shadow {
box-shadow: 0 -4px #fff, 0 -8px #000, 4px 0 #fff, 4px -4px #000, 8px 0 #000, 0 4px #fff, 0 8px #000, -4px 0 #fff, -4px 4px #000, -8px 0 #000, -4px -4px #000, 4px 4px #000, 4px 12px rgba(0, 0, 0, 0.1), 12px 4px rgba(0, 0, 0, 0.1), 8px 8px rgba(0, 0, 0, 0.1);
}
.bubble.mini {
width: 110px;
font-size: 16px;
padding: 4px;
font-family: monospace;
}
.bubble.medium {
width: 350px;
}
.bubble.large {
width: 560px;
font-size: 24px;
text-align: left;
text-transform: uppercase;
}
.bubble.grow {
width: initial;
}
.bubble.top::after {
height: 4px;
width: 4px;
top: -8px;
left: 32px;
box-shadow: 0 -4px #000, 0 -8px #000, 0 -12px #000, 0 -16px #000, -4px -12px #000, -8px -8px #000, -12px -4px #000, -4px -4px #fff, -8px -4px #fff, -4px -8px #fff, -4px 0 #fff, -8px 0 #fff, -12px 0 #fff;
}
.bubble.right::after {
height: 4px;
width: 4px;
top: 84px;
right: -8px;
background: white;
box-shadow: 4px -4px #fff, 4px 0 #fff, 8px 0 #fff, 0 -8px #fff, 4px 4px #000, 8px 4px #000, 12px 4px #000, 16px 4px #000, 12px 0 #000, 8px -4px #000, 4px -8px #000, 0 -4px #fff;
}
.bubble.bottom::after {
height: 4px;
width: 4px;
bottom: -8px;
left: 32px;
box-shadow: 0 4px #000, 0 8px #000, 0 12px #000, 0 16px #000, -4px 12px #000, -8px 8px #000, -12px 4px #000, -4px 4px #fff, -8px 4px #fff, -4px 8px #fff, -4px 0 #fff, -8px 0 #fff, -12px 0 #fff;
}
.bubble.left::after {
height: 4px;
width: 4px;
top: 20px;
left: -8px;
background: white;
box-shadow: -4px -4px #fff, -4px 0 #fff, -8px 0 #fff, 0 -8px #fff, -4px 4px #000, -8px 4px #000, -12px 4px #000, -16px 4px #000, -12px 0 #000, -8px -4px #000, -4px -8px #000, 0 -4px #fff;
}
.bubble textarea {
width: 100%;
height: 6em;
border: none;
}
.bubble textarea:focus {
outline: none !important;
border: none;
/* box-shadow: 0 0 10px #719ECE; */
}
canvas.creature {
width: 64px;
height: 64px;
image-rendering: optimizeSpeed;
image-rendering: -moz-crisp-edges;
image-rendering: -webkit-optimize-contrast;
image-rendering: -o-crisp-edges;
image-rendering: pixelated;
-ms-interpolation-mode: nearest-neighbor;
border: none;
}

@ -0,0 +1,332 @@
{
"intro": [
"I'm a(n)",
"I am a(n)",
"I aim to be a(n)",
"Some call me a(n)",
"I'm known to be a(n)",
"Someday I will be a(n)"
],
"action": [
"researching",
"studying",
"scanning",
"inflitrating",
"uploading",
"seeding",
"screening",
"spamming",
"tweeting",
"tooting",
"singing",
"feeling",
"listening to",
"playing",
"reading",
"disrupting",
"acting as",
"dancing",
"seeing",
"cooking",
"preaching",
"translating",
"yelling",
"screaming",
"running",
"whistleblowing",
"leaking",
"painting",
"writing",
"laughing at",
"dictating",
"designing",
"destroying",
"showing",
"debating",
"creating",
"performing",
"building",
"hiding",
"surveilling",
"faking",
"banning",
"watching",
"deciding",
"dealing",
"exchanging",
"mediating",
"fighting",
"vandalizing",
"parodying",
"questioning",
"criticizing",
"provoking",
"obscuring",
"shouting on",
"controlling",
"demanding",
"delivering",
"disclosing",
"developing",
"divulging",
"lying about",
"bragging about",
"echoing",
"portraying",
"helping",
"hiding",
"interrupting",
"conspiring",
"sending",
"repairing",
"disagreeing"
],
"actor": [
"hipster",
"scener",
"seeder",
"leecher",
"pirate",
"librarian",
"virus",
"AI",
"wizard",
"quant",
"CEO",
"shareholder",
"user",
"stenographer",
"maker",
"artist",
"professional",
"hobbyist",
"amateur",
"teacher",
"student",
"doctor",
"patient",
"public",
"criminal",
"cook",
"editor",
"programmer",
"preacher",
"livecoder",
"individual",
"visitor",
"hacker",
"reader",
"consumer",
"mediator",
"actor",
"builder",
"child",
"adult",
"architect",
"tourist",
"person",
"parent",
"photographer",
"musician",
"designer",
"chatbot",
"regulator",
"dancer",
"developer",
"author",
"robot",
"dog",
"cat",
"judge",
"celebrity",
"computer",
"politician",
"president",
"manager",
"entrepreneur",
"whistleblower",
"infiltrator",
"book binder",
"collective"
],
"media": [
"image boards",
"forums",
"warez",
"screens",
"galleries",
"screensavers",
"viruses",
"frequencies",
"files",
"operating systems",
"databases",
"privacy",
"posters",
"cybernetics",
"floppy disks",
"CD-ROMs",
"LaserDiscs",
"apps",
"spam",
"startups",
"manuscripts",
"carpets",
"markets",
"architectures",
"feels",
"books",
"games",
"radio",
"music",
"graffitis",
"blogs",
"theatre",
"woodblocks",
"dances",
"recycling",
"emails",
"food",
"migration",
"magazines",
"maps",
"comics",
"finance",
"teaching",
"memes",
"futures",
"traditions",
"corruption",
"regulation",
"porn",
"webpages",
"vandalism",
"samizdat",
"ethics",
"billboards",
"advertising",
"manifestos",
"leaflets",
"browsers",
"wikis",
"canvases",
"tapes",
"vinyls",
"records",
"synthesizers",
"feeds",
"newspapers",
"HTML pages",
"codex",
"software",
"codes of conduct",
"epubs",
"PDFs",
"NFTs",
"stories",
"dictionaries",
"libraries",
"bugs",
"disagreements"
],
"prefix": [
"proto",
"xeno",
"cyber",
"retro",
"pre",
"ante",
"proto",
"post",
"avant",
"neo",
"anti",
"co",
"epi",
"ex",
"extra",
"hyper",
"infra",
"inter",
"macro",
"micro",
"mid",
"mono",
"non",
"multi",
"omni",
"semi",
"sub",
"super",
"trans",
"cross",
"uni",
"minor"
],
"sort": [
"bootleg",
"capitalist",
"progressive",
"organic",
"industrial",
"homemade",
"craft",
"AI",
"encrypted",
"inclusive",
"fair",
"transparent",
"command line",
"granular",
"crypto",
"centralised",
"free",
"libre",
"open",
"proprietary",
"DIY",
"DIWO",
"homebrew",
"decentralised",
"closed source",
"binary",
"plaintext",
"federated",
"distributed",
"online",
"offline",
"raw",
"compressed",
"deep",
"dark",
"common",
"shared",
"liberal",
"anarcho-capitalist",
"anarcho-communist",
"anarcho-syndicalist",
"authoritarian",
"capitalist",
"communist",
"marxist",
"apolitical",
"syndicated",
"digital",
"analog",
"bottom-up",
"top-down",
"horizontal",
"vertical",
"agonistic",
"version-controlled",
"feminist",
"decolonial",
"contradictory",
"occasional",
"layered",
"entangled",
"collective",
"complex",
"artificial"
]
}

@ -0,0 +1,324 @@
intro:
- I'm a(n)
- I am a(n)
- I aim to be a(n)
- Some call me a(n)
- I'm known to be a(n)
- Someday I will be a(n)
action:
- researching
- studying
- scanning
- inflitrating
- uploading
- seeding
- screening
- spamming
- tweeting
- tooting
- singing
- feeling
- listening to
- playing
- reading
- disrupting
- acting as
- dancing
- seeing
- cooking
- preaching
- translating
- yelling
- screaming
- running
- whistleblowing
- leaking
- painting
- writing
- laughing at
- dictating
- designing
- destroying
- showing
- debating
- creating
- performing
- building
- hiding
- surveilling
- faking
- banning
- watching
- deciding
- dealing
- exchanging
- mediating
- fighting
- vandalizing
- parodying
- questioning
- criticizing
- provoking
- obscuring
- shouting on
- controlling
- demanding
- delivering
- disclosing
- developing
- divulging
- lying about
- bragging about
- echoing
- portraying
- helping
- hiding
- interrupting
- conspiring
- sending
- repairing
- disagreeing
actor:
- hipster
- scener
- seeder
- leecher
- pirate
- librarian
- virus
- AI
- wizard
- quant
- CEO
- shareholder
- user
- stenographer
- maker
- artist
- professional
- hobbyist
- amateur
- teacher
- student
- doctor
- patient
- public
- criminal
- cook
- editor
- programmer
- preacher
- livecoder
- individual
- visitor
- hacker
- reader
- consumer
- mediator
- actor
- builder
- child
- adult
- architect
- tourist
- person
- parent
- photographer
- musician
- designer
- chatbot
- regulator
- dancer
- developer
- author
- robot
- dog
- cat
- judge
- celebrity
- computer
- politician
- president
- manager
- entrepreneur
- whistleblower
- infiltrator
- book binder
- collective
media:
- image boards
- forums
- warez
- screens
- galleries
- screensavers
- viruses
- frequencies
- files
- operating systems
- databases
- privacy
- posters
- cybernetics
- floppy disks
- CD-ROMs
- LaserDiscs
- apps
- spam
- startups
- manuscripts
- carpets
- markets
- architectures
- feels
- books
- games
- radio
- music
- graffitis
- blogs
- theatre
- woodblocks
- dances
- recycling
- emails
- food
- migration
- magazines
- maps
- comics
- finance
- teaching
- memes
- futures
- traditions
- corruption
- regulation
- porn
- webpages
- vandalism
- samizdat
- ethics
- billboards
- advertising
- manifestos
- leaflets
- browsers
- wikis
- canvases
- tapes
- vinyls
- records
- synthesizers
- feeds
- newspapers
- HTML pages
- codex
- software
- codes of conduct
- epubs
- PDFs
- NFTs
- stories
- dictionaries
- libraries
- bugs
- disagreements
prefix:
- proto
- xeno
- cyber
- retro
- pre
- ante
- proto
- post
- avant
- neo
- anti
- co
- epi
- ex
- extra
- hyper
- infra
- inter
- macro
- micro
- mid
- mono
- non
- multi
- omni
- semi
- sub
- super
- trans
- cross
- uni
- minor
sort:
- bootleg
- capitalist
- progressive
- organic
- industrial
- homemade
- craft
- AI
- encrypted
- inclusive
- fair
- transparent
- command line
- granular
- crypto
- centralised
- free
- libre
- open
- proprietary
- DIY
- DIWO
- homebrew
- decentralised
- closed source
- binary
- plaintext
- federated
- distributed
- online
- offline
- raw
- compressed
- deep
- dark
- common
- shared
- liberal
- anarcho-capitalist
- anarcho-communist
- anarcho-syndicalist
- authoritarian
- capitalist
- communist
- marxist
- apolitical
- syndicated
- digital
- analog
- bottom-up
- top-down
- horizontal
- vertical
- agonistic
- version-controlled
- feminist
- decolonial
- contradictory
- occasional
- layered
- entangled
- collective
- complex
- artificial

@ -0,0 +1,16 @@
import click
import yaml
import json
@click.command()
@click.argument('input', type=click.File('r'))
@click.argument('output', type=click.File('w'))
@click.argument('indent', type=int, default=2)
def yaml_to_json(input, output, indent):
"""Convert a YAML input to JSON output"""
d = yaml.safe_load(input)
json.dump(d, output, indent=indent)
if __name__ == '__main__':
yaml_to_json()

@ -0,0 +1,9 @@
canvas {
image-rendering: optimizeSpeed;
image-rendering: -moz-crisp-edges;
image-rendering: -webkit-optimize-contrast;
image-rendering: -o-crisp-edges;
image-rendering: pixelated;
-ms-interpolation-mode: nearest-neighbor;
border: 1px solid black;
}

@ -0,0 +1,28 @@
var stage = new createjs.Stage('canvas');
// var shape = new createjs.Shape();
// shape.graphics.beginFill('red').drawRect(0, 0, 120, 120);
// stage.addChild(shape);
// stage.update();
var ss = new createjs.SpriteSheet({
frames: {
width: 16,
height: 16,
regX: 0,
regY: 0,
spacing: 0,
margin: 0,
count: 6
},
animations: {stand: 0, run: [0, 3]},
images: [document.querySelector("#creature-src")]
});
var sprite = new createjs.Sprite(ss, "stand");
sprite.scaleY = sprite.scaleX = 4;
stage.addChild(sprite);
sprite.on("click", function() { sprite.gotoAndPlay("jump"); });
createjs.Ticker.on("tick", stage);

@ -0,0 +1,123 @@
// function prefixInputWithASpan(input, text){
// var span = document.createElement("span");
// span.innerText = text;
// input.parentElement.prepend(span);
// }
let terms;
let cParts = {};
function setupTextInput(key){
var input = document.getElementById(key + "Input");
prefixInputWithASpan(input, key)
input.setAttribute('min', 0);
input.setAttribute('max', terms[key].length - 1);
input.value = parseInt(Math.random() * (terms[key].length + 1));
var inputUpdate = function(e){
// gParts[key] = parseInt(e.target.value);
// console.log("INPUT UPDATE", e.target.value, gParts);
// updateOutCanvas();
console.log("INPUT UPDATE", e.target.value);
cParts[key] = parseInt(e.target.value);
updateCaption();
}
inputUpdate({ target: input });
input.onchange = inputUpdate;
input.oninput = inputUpdate;
};
let grammar = ["intro", "actor", "action", "prefix", "sort", "media"];
function updateCaption () {
let caption = "";
for (let gi=grammar.length-1; gi>=0; gi--) {
let key = grammar[gi];
let term = terms[key][cParts[key]];
if (!term) break;
console.log(`key: ${key}, term: ${term}`)
if (term.match(/a\(n\)/)) {
console.log(`doing a(n) replacement on ${term}, for [${caption}]`);
if (caption.match(/^[aeiou]/i)) {
console.log("VOWEL");
term = term.replace(/a\(n\)/, "an");
} else {
term = term.replace(/a\(n\)/, "a");
}
}
caption = term + (caption?" ":"") + caption;
}
// grammar.forEach(key => {
// caption += (caption ? " " : "") + terms[key][cParts[key]];
// })
document.querySelector("#caption").innerHTML = caption;
}
async function start () {
const src = document.querySelector("a#terms-data-source").href;
terms = await (await fetch(src)).json();
console.log("terms", terms);
setupTextInput("intro");
setupTextInput("action");
setupTextInput("actor");
setupTextInput("media");
setupTextInput("prefix");
setupTextInput("sort");
}
var randomizebtn = document.getElementById('randomizebtn');
randomizebtn.onclick = function() {
let inputs = document.getElementById('inputs').querySelectorAll('input.g');
for(var i=0; i<inputs.length; i++){
var input = inputs[i];
var key = input.id.replace("Input", "");
var max = PARTS_MAX[key];
var randval = Math.floor(Math.random() * (max + 1));
input.value = randval;
gParts[key] = randval;
if (key == 'color') {
// gColor = randval;
input.dispatchEvent(new Event('input', { 'bubbles': true }));
}
console.log("randomizing", input, randval);
}
updateOutCanvas();
inputs = document.getElementById('inputs').querySelectorAll('input.c');
for(var i=0; i<inputs.length; i++){
var input = inputs[i];
var key = input.id.replace("Input", "");
var max = terms[key].length;
var randval = Math.floor(Math.random() * (max + 0));
input.value = randval;
cParts[key] = randval;
}
updateCaption();
}
start();
function send (canvas, form) {
canvas.toBlob(blob => {
if (blob) {
const formdata = new FormData();
const filename = new Date().toISOString() + ".png";
formdata.append("image", blob, filename);
formdata.append("caption", document.querySelector("#caption").textContent);
formdata.append("message", form.message.value);
formdata.append("csrfmiddlewaretoken", form.csrfmiddlewaretoken.value);
fetch(form.action, { method:"POST", body: formdata })
.then(() => {
document.querySelector("#inputs").style.display = "none";
document.querySelector("#caption").disabled = true;
});
}
}, "image/png");
}
document.querySelector("button#send").addEventListener("click", () => {
console.log("SEND");
const form = document.querySelector("form");
send(outCanvas, form);
})

@ -0,0 +1,13 @@
<!doctype html>
<html lang=en>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, user-scalable=no">
<title>{% block title %}study at XPUB!{% endblock %}</title>
<style>{%block style %}{% endblock %}</style>
{%block extrahead %}{% endblock %}
</head>
<body>
{%block content %}
{% endblock %}
</body>
</html>

@ -0,0 +1,19 @@
{% load static %}
<!doctype html>
<html lang=en>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, user-scalable=no">
<title>xpub call 2025</title>
<style>
</style>
</head>
<body>
<img src="{{creature.image.url}}">
<p>{{creature.caption}}</p>
</body>
</html>

@ -0,0 +1,6 @@
{% extends "call/base.html" %}
{% block content %}
<img src="{{object.image.url}}">
<p>{{object.caption}}</p>
{% endblock %}

@ -0,0 +1,30 @@
{% extends "call/base.html" %}
{% block content %}
<h2>Creatures</h2>
<ul>
{% for creature in page_obj %}
<li><a href="{% url 'walk' creature.id %}">{{ creature.caption }}</a></li>
{% endfor %}
</ul>
<div class="pagination">
<span class="step-links">
{% if page_obj.has_previous %}
<a href="?page=1">&laquo; first</a>
<a href="?page={{ page_obj.previous_page_number }}">previous</a>
{% endif %}
<span class="current">
Page {{ page_obj.number }} of {{ page_obj.paginator.num_pages }}.
</span>
{% if page_obj.has_next %}
<a href="?page={{ page_obj.next_page_number }}">next</a>
<a href="?page={{ page_obj.paginator.num_pages }}">last &raquo;</a>
{% endif %}
</span>
</div>
{% endblock %}

@ -0,0 +1,33 @@
{% extends "call/base.html" %}
{% load static %}
{% block extrahead %}
<link rel="stylesheet" href="{%static 'call/guestbook.css' %}">
{% endblock %}
{% block content %}
<marquee><h1>Welcome XPUB Class of 2025-2027</h1></marquee>
<div id="content">
{% for creature in page_obj %}
<img class="creature" src="{{creature.image.url}}" title="{{creature.caption}}">
{% endfor %}
</div>
<div class="pagination">
<span class="step-links">
{% if page_obj.has_previous %}
<a href="?page=1">&laquo; first</a>
<a href="?page={{ page_obj.previous_page_number }}">previous</a>
{% endif %}
<span class="current">
Page {{ page_obj.number }} of {{ page_obj.paginator.num_pages }}.
</span>
{% if page_obj.has_next %}
<a href="?page={{ page_obj.next_page_number }}">next</a>
<a href="?page={{ page_obj.paginator.num_pages }}">last &raquo;</a>
{% endif %}
</span>
</div>
<script src="{% static 'call/guestbook.js' %}"></script>
{% endblock %}

File diff suppressed because one or more lines are too long

@ -0,0 +1,57 @@
{% load static %}
{% block extrahead %}
<link rel="stylesheet" href="{% static 'call/statement.css' %}">
{% endblock %}
{% block content %}
<img src="{{creature.image.url}}" class="creature" style="display: none">
<canvas class="creature"></canvas>
<!-- <p>{{creature.caption}}</p> -->
<!-- <div class="bubble mini top">awesome!</div>
<div class="bubble right">I can get you a thumb by three o'clock</div>
<div class="bubble medium bottom">I'm not into the whole China thing, man</div>
<div class="bubble grow left">This is such a travesty</div>
<div class="bubble shadow large bottom">Ik moet nu eerst even een gerestaureerde drol hebben!</div> -->
<div class="bubble shadow large top"><textarea placeholder="Your messsage" autofocus id="caption"></textarea></div>
<p>The Master Experimental Publishing at the Piet Zwart Institute in Rotterdam (NL) is a two-year master focused on the acts of making things public and creating publics in the age of post-digital networks.</p>
<div id="info">
<p>Application deadlines:</p>
<p>
March 4, 2024 (non-EU + EU)<br>
May 8, 2024 (EU)<br>
</p>
<p>Open days:</p>
<p>
<a href="mailto:pzi.coordinators@hr.nl?subject=XPUB Open Day (online)&amp;body=Hello, I will join the online open day on the February 6th on 10:00 CET or 16:00 CET" target="_blank">February 6, 2024</a> (online)<br>
<a href="https://www.pzwart.nl/blog/2023/10/10/open-day/" target="_blank">February 15, 2025</a> (IRL)<br>
</p>
<p>
<br>
<button id="apply">Apply</button>
</p>
</div>
<p>CSS Speech bubbles from <a href="https://codepen.io/josfabre/pen/EBMWwW">Jos Faber</a>.</p>
<script>
// Get canvas context
const ctx = document.querySelector("canvas.creature").getContext("2d");
// Load image
const image = new Image();
image.onload = () => {
// Draw the image into the canvas
ctx.drawImage(image, 0, 0, 64);
};
image.src = document.querySelector("img.creature").src;
</script>
{% endblock %}

@ -0,0 +1,14 @@
{% extends "call/base.html" %}
{% load static %}
{% block extrahead %}
<link rel="stylesheet" href="{% static 'call/walk.css' %}">
<script src="{% static 'EaselJS-1.0.0/lib/easeljs.js' %}"></script>
<script src="{% static 'TweenJS-1.0.0/lib/tweenjs.js' %}"></script>
{% endblock %}
{% block content %}
{{object}} {{object.image}} {{object.image.url}}
<div><img id="creature-src" src="{{object.image.url}}"></div>
<canvas id="canvas" width="550" height="250" style="border: 1px dotted gray"></canvas>
<script src="{% static 'call/walk.js' %}"></script>
{% endblock %}

@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

@ -0,0 +1,28 @@
"""
URL configuration for call2025 project.
The `urlpatterns` list routes URLs to views. For more information please see:
https://docs.djangoproject.com/en/5.1/topics/http/urls/
Examples:
Function views
1. Add an import: from my_app import views
2. Add a URL to urlpatterns: path('', views.home, name='home')
Class-based views
1. Add an import: from other_app.views import Home
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
Including another URLconf
1. Import the include() function: from django.urls import include, path
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
"""
from django.contrib import admin
from django.urls import path, include
from call import views
from .views import Guestbook, CreatureDetail, Statement, Walk
urlpatterns = [
path("guestbook/", Guestbook.as_view(), name="guestbook"),
path("creatures/<int:pk>/", CreatureDetail.as_view(), name="creature_detail"),
path("creatures/<int:pk>/s/", Statement.as_view(), name="statement"),
path("creatures/<int:pk>/w/", Walk.as_view(), name="walk"),
path('', views.index)
]

@ -0,0 +1,41 @@
from django.shortcuts import render, HttpResponseRedirect
from call.forms import CreatureForm
from django.views import generic
from .models import Creature
from django.urls import reverse
# Create your views here.
def index(request):
if request.method == "POST":
print ("post", request.POST)
print ("files", request.FILES)
form = CreatureForm(request.POST, request.FILES)
# form = CreatureFormNo(request.POST, request.FILES)
# formset = AuthorFormSet(request.POST, request.FILES)
if form.is_valid():
print ("FORM IS VALID")
form.save()
return HttpResponseRedirect(reverse("statement", args=(form.instance.id,)))
# do something.
else:
print ("FORM NOT VALID")
else:
form = CreatureForm()
return render(request, "call/index.html", {"form": form})
class Guestbook(generic.ListView):
paginate_by = 20
model=Creature
template_name = "call/guestbook.html"
class CreatureDetail(generic.DetailView):
model = Creature
class Statement(generic.DetailView):
model = Creature
template_name = "call/statement.html"
class Walk(generic.DetailView):
model = Creature
template_name = "call/walk.html"

@ -0,0 +1,16 @@
"""
ASGI config for call2025 project.
It exposes the ASGI callable as a module-level variable named ``application``.
For more information on this file, see
https://docs.djangoproject.com/en/5.1/howto/deployment/asgi/
"""
import os
from django.core.asgi import get_asgi_application
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'call2025.settings')
application = get_asgi_application()

@ -0,0 +1,131 @@
"""
Django settings for call2025 project.
Generated by 'django-admin startproject' using Django 5.1.5.
For more information on this file, see
https://docs.djangoproject.com/en/5.1/topics/settings/
For the full list of settings and their values, see
https://docs.djangoproject.com/en/5.1/ref/settings/
"""
from pathlib import Path
# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/5.1/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'django-insecure-nn7mvrgu6de^%z&6t1usgaji_*59z*v+230tluec-h4fw*n(%i'
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
ALLOWED_HOSTS = []
# Application definition
INSTALLED_APPS = [
'call',
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
]
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
ROOT_URLCONF = 'call2025.urls'
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
WSGI_APPLICATION = 'call2025.wsgi.application'
# Database
# https://docs.djangoproject.com/en/5.1/ref/settings/#databases
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'db' / 'db.sqlite3',
}
}
# Password validation
# https://docs.djangoproject.com/en/5.1/ref/settings/#auth-password-validators
AUTH_PASSWORD_VALIDATORS = [
{
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
},
]
# Internationalization
# https://docs.djangoproject.com/en/5.1/topics/i18n/
LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'UTC'
USE_I18N = True
USE_TZ = True
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/5.1/howto/static-files/
# STATIC_URL = 'static/'
STATIC_URL = '/call/static/'
STATIC_ROOT = "/var/www/wsgi/call2025/static/"
# Default primary key field type
# https://docs.djangoproject.com/en/5.1/ref/settings/#default-auto-field
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
MEDIA_ROOT = 'media/'
MEDIA_ROOT = "/var/www/wsgi/call2025/media/"
MEDIA_URL = 'media/'
MEDIA_URL = '/call/media/'

@ -0,0 +1,138 @@
"""
Django settings for call2025 project.
Generated by 'django-admin startproject' using Django 5.1.5.
For more information on this file, see
https://docs.djangoproject.com/en/5.1/topics/settings/
For the full list of settings and their values, see
https://docs.djangoproject.com/en/5.1/ref/settings/
"""
from pathlib import Path
# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/5.1/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'django-insecure-nn7mvrgu6de^%z&6t1usgaji_*59z*v+230tluec-h4fw*n(%i'
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
ALLOWED_HOSTS = []
# Application definition
INSTALLED_APPS = [
'call',
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
]
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
ROOT_URLCONF = 'call2025.urls'
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
WSGI_APPLICATION = 'call2025.wsgi.application'
# Database
# https://docs.djangoproject.com/en/5.1/ref/settings/#databases
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'db' / 'db.sqlite3',
}
}
# Password validation
# https://docs.djangoproject.com/en/5.1/ref/settings/#auth-password-validators
AUTH_PASSWORD_VALIDATORS = [
{
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
},
]
# Internationalization
# https://docs.djangoproject.com/en/5.1/topics/i18n/
LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'UTC'
USE_I18N = True
USE_TZ = True
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/5.1/howto/static-files/
#
DEPLOY = False
if DEPLOY:
STATIC_URL = '/call/static/'
STATIC_ROOT = "/var/www/wsgi/call2025/static/"
else:
STATIC_URL = 'static/'
# Default primary key field type
# https://docs.djangoproject.com/en/5.1/ref/settings/#default-auto-field
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
if DEPLOY:
MEDIA_URL = '/call/media/'
MEDIA_ROOT = "/var/www/wsgi/call2025/media/"
else:
MEDIA_ROOT = 'media/'
MEDIA_URL = 'media/'

@ -0,0 +1,26 @@
"""
URL configuration for call2025 project.
The `urlpatterns` list routes URLs to views. For more information please see:
https://docs.djangoproject.com/en/5.1/topics/http/urls/
Examples:
Function views
1. Add an import: from my_app import views
2. Add a URL to urlpatterns: path('', views.home, name='home')
Class-based views
1. Add an import: from other_app.views import Home
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
Including another URLconf
1. Import the include() function: from django.urls import include, path
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
"""
from django.contrib import admin
from django.urls import path, include
from django.conf import settings
from django.conf.urls.static import static
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('call.urls'))
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

@ -0,0 +1,16 @@
"""
WSGI config for call2025 project.
It exposes the WSGI callable as a module-level variable named ``application``.
For more information on this file, see
https://docs.djangoproject.com/en/5.1/howto/deployment/wsgi/
"""
import os
from django.core.wsgi import get_wsgi_application
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'call2025.settings')
application = get_wsgi_application()

Binary file not shown.

After

Width:  |  Height:  |  Size: 308 B

@ -0,0 +1,50 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="2.8504503mm"
height="3.0193765mm"
viewBox="0 0 2.8504503 3.0193765"
version="1.1"
id="svg1"
inkscape:version="1.4 (e7c3feb100, 2024-10-09)"
sodipodi:docname="corner.svg"
inkscape:export-filename="corner.png"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview1"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="true"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="mm"
inkscape:zoom="32.494215"
inkscape:cx="7.7552266"
inkscape:cy="11.786713"
inkscape:window-width="1920"
inkscape:window-height="1008"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="layer1" />
<defs
id="defs1" />
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-47.887113,-41.55363)">
<path
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.264583;stroke-linecap:square;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
d="m 48.027547,44.560578 v -2.60763 l 2.624924,2.60763"
id="path1" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

@ -0,0 +1,22 @@
#!/usr/bin/env python
"""Django's command-line utility for administrative tasks."""
import os
import sys
def main():
"""Run administrative tasks."""
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'call2025.settings')
try:
from django.core.management import execute_from_command_line
except ImportError as exc:
raise ImportError(
"Couldn't import Django. Are you sure it's installed and "
"available on your PYTHONPATH environment variable? Did you "
"forget to activate a virtual environment?"
) from exc
execute_from_command_line(sys.argv)
if __name__ == '__main__':
main()
Loading…
Cancel
Save