2025 call django

master
Michael Murtaugh 18 hours ago
parent 5cae3e2542
commit a9a66fe27e

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

@ -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,10 @@
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)

@ -0,0 +1,394 @@
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 = {
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 = "run_front";
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

@ -0,0 +1,324 @@
{
"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,317 @@
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,102 @@
// 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 = ["actor", "action", "prefix", "sort", "media"];
function updateCaption () {
let 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("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 });
}
}, "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 %}xpub call 2025{% endblock %}</title>
<style>
</style>
</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 'creature_detail' 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 %}

File diff suppressed because one or more lines are too long

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

@ -0,0 +1,27 @@
"""
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 CreatureIndex, CreatureDetail
urlpatterns = [
path("creatures/", CreatureIndex.as_view(), name="creature_list"),
path("creatures/<int:pk>/", CreatureDetail.as_view(), name="creature_detail"),
path('', views.index)
]

@ -0,0 +1,29 @@
from django.shortcuts import render
from call.forms import CreatureForm
from django.views import generic
from .models import Creature
# 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()
# do something.
else:
print ("FORM NOT VALID")
else:
form = CreatureForm()
return render(request, "call/index.html", {"form": form})
class CreatureIndex(generic.ListView):
paginate_by = 20
model=Creature
class CreatureDetail(generic.DetailView):
model = Creature

@ -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,127 @@
"""
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/'
# 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_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()

@ -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