commit 646e3d51a3dde908893d5ccac1b16ff8ff153166 Author: Michael Murtaugh Date: Tue Jan 28 10:40:13 2025 +0100 first commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6e147d7 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +db/ +**/__pycache__/ +media/ +venv/ diff --git a/call/NOTES.md b/call/NOTES.md new file mode 100644 index 0000000..b9ac33f --- /dev/null +++ b/call/NOTES.md @@ -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" + + + diff --git a/call/__init__.py b/call/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/call/admin.py b/call/admin.py new file mode 100644 index 0000000..b42272c --- /dev/null +++ b/call/admin.py @@ -0,0 +1,5 @@ +from django.contrib import admin + +from .models import Creature + +admin.site.register(Creature) \ No newline at end of file diff --git a/call/apps.py b/call/apps.py new file mode 100644 index 0000000..9766d9c --- /dev/null +++ b/call/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class CallConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'call' diff --git a/call/forms.py b/call/forms.py new file mode 100644 index 0000000..5f054c2 --- /dev/null +++ b/call/forms.py @@ -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"] + diff --git a/call/migrations/0001_initial.py b/call/migrations/0001_initial.py new file mode 100644 index 0000000..439c774 --- /dev/null +++ b/call/migrations/0001_initial.py @@ -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)), + ], + ), + ] diff --git a/call/migrations/0002_alter_creature_message.py b/call/migrations/0002_alter_creature_message.py new file mode 100644 index 0000000..3e313cd --- /dev/null +++ b/call/migrations/0002_alter_creature_message.py @@ -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), + ), + ] diff --git a/call/migrations/__init__.py b/call/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/call/models.py b/call/models.py new file mode 100644 index 0000000..df2d824 --- /dev/null +++ b/call/models.py @@ -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"] diff --git a/call/static/call/2bitcharactergenerator.js b/call/static/call/2bitcharactergenerator.js new file mode 100644 index 0000000..1830a2d --- /dev/null +++ b/call/static/call/2bitcharactergenerator.js @@ -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 + + + +xpub call 2025 + + + + + +

XPUB Prospective Student Builder...

+loading... +
+ + +
+ +
HERE CAPTION
+ +
+ + + + + + + +
+ + +
+ + + + + + + + +
+ +
+ + + + +
+
+ export: + default + gbs + gbs anim +
+ +
+ +
+ + + + + + + +
+ + +
+

+ The heart of this webpage is 0x72's 2bitcharactergenerator, found via Everest Pipkin's great tinytools.directory and generously released via a CC0 (public domain) license. As a program XPUB believes in creative expressions as not from scratch, but, like language in general, shared and in conversation with all that's come before it. +

+
+ + + + + + diff --git a/call/static/call/corner.png b/call/static/call/corner.png new file mode 100644 index 0000000..0770538 Binary files /dev/null and b/call/static/call/corner.png differ diff --git a/call/static/call/guestbook.css b/call/static/call/guestbook.css new file mode 100644 index 0000000..cac43f6 --- /dev/null +++ b/call/static/call/guestbook.css @@ -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; +} + \ No newline at end of file diff --git a/call/static/call/guestbook.js b/call/static/call/guestbook.js new file mode 100644 index 0000000..99eb6b1 --- /dev/null +++ b/call/static/call/guestbook.js @@ -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); +}) \ No newline at end of file diff --git a/call/static/call/index.css b/call/static/call/index.css new file mode 100644 index 0000000..987d8a3 --- /dev/null +++ b/call/static/call/index.css @@ -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; +} \ No newline at end of file diff --git a/call/static/call/statement.css b/call/static/call/statement.css new file mode 100644 index 0000000..679ae3c --- /dev/null +++ b/call/static/call/statement.css @@ -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; +} \ No newline at end of file diff --git a/call/static/call/terms.json b/call/static/call/terms.json new file mode 100644 index 0000000..2238873 --- /dev/null +++ b/call/static/call/terms.json @@ -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" + ] +} \ No newline at end of file diff --git a/call/static/call/terms.yaml b/call/static/call/terms.yaml new file mode 100644 index 0000000..45fd849 --- /dev/null +++ b/call/static/call/terms.yaml @@ -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 diff --git a/call/static/call/update_terms_yaml_to_json.py b/call/static/call/update_terms_yaml_to_json.py new file mode 100644 index 0000000..af47f44 --- /dev/null +++ b/call/static/call/update_terms_yaml_to_json.py @@ -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() diff --git a/call/static/call/walk.css b/call/static/call/walk.css new file mode 100644 index 0000000..8b0122d --- /dev/null +++ b/call/static/call/walk.css @@ -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; +} \ No newline at end of file diff --git a/call/static/call/walk.js b/call/static/call/walk.js new file mode 100644 index 0000000..9badebe --- /dev/null +++ b/call/static/call/walk.js @@ -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); diff --git a/call/static/call/xpubcharacters.js b/call/static/call/xpubcharacters.js new file mode 100644 index 0000000..e8e4c54 --- /dev/null +++ b/call/static/call/xpubcharacters.js @@ -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 { + 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); +}) \ No newline at end of file diff --git a/call/templates/call/base.html b/call/templates/call/base.html new file mode 100644 index 0000000..a76d3b4 --- /dev/null +++ b/call/templates/call/base.html @@ -0,0 +1,13 @@ + + + + +{% block title %}study at XPUB!{% endblock %} + +{%block extrahead %}{% endblock %} + + +{%block content %} +{% endblock %} + + \ No newline at end of file diff --git a/call/templates/call/creature.html b/call/templates/call/creature.html new file mode 100644 index 0000000..b5435a0 --- /dev/null +++ b/call/templates/call/creature.html @@ -0,0 +1,19 @@ +{% load static %} + + + + +xpub call 2025 + + + + + + + +

{{creature.caption}}

+ + + + \ No newline at end of file diff --git a/call/templates/call/creature_detail.html b/call/templates/call/creature_detail.html new file mode 100644 index 0000000..f38af06 --- /dev/null +++ b/call/templates/call/creature_detail.html @@ -0,0 +1,6 @@ +{% extends "call/base.html" %} + +{% block content %} + +

{{object.caption}}

+{% endblock %} \ No newline at end of file diff --git a/call/templates/call/creature_list.html b/call/templates/call/creature_list.html new file mode 100644 index 0000000..7c33a38 --- /dev/null +++ b/call/templates/call/creature_list.html @@ -0,0 +1,30 @@ +{% extends "call/base.html" %} + +{% block content %} +

Creatures

+ + + + + +{% endblock %} \ No newline at end of file diff --git a/call/templates/call/guestbook.html b/call/templates/call/guestbook.html new file mode 100644 index 0000000..ae613b4 --- /dev/null +++ b/call/templates/call/guestbook.html @@ -0,0 +1,33 @@ +{% extends "call/base.html" %} +{% load static %} +{% block extrahead %} + +{% endblock %} +{% block content %} +

Welcome XPUB Class of 2025-2027

+
+ {% for creature in page_obj %} + + {% endfor %} +
+ + + + +{% endblock %} \ No newline at end of file diff --git a/call/templates/call/index.html b/call/templates/call/index.html new file mode 100644 index 0000000..a45ad75 --- /dev/null +++ b/call/templates/call/index.html @@ -0,0 +1,117 @@ +{% extends "call/base.html" %} +{% load static %} +{% block extrahead %} + +{% endblock %} + +{% block content %} + +

XPUB Community Builder...

+loading... +
+ + +
+ +
+ + + + + + + + + + + + + + +
+ + +
+ +
+ + +
+
+ +
+ +
+ + + + +
+
+ export: + default + gbs + gbs anim +
+ +
+ + + + + + + +
+ +
+ +
+ +
+

+ The heart of this webpage is the 2bitcharactergenerator, by 0x72, + found via Everest Pipkin's great tinytools.directory and generously released via a CC0 (public domain) license. + The generative text is inspired by early experiments with computer and poetry such as Allison Knowle's House of Dust. + As a program XPUB believes in creative expression as not from scratch, and like language, shared and in conversation with all that has come before. +

+
+ +
+

+ The Master Experimental Publishing (XPUB) 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. +

+

Application deadlines:

+

+

    +
  • March 4, 2024 (non-EU final + EU priority deadline)
  • +
  • May 8, 2024 (final EU deadline)
  • +
+

+

Open days:

+

+

+

+

+ +

+
+ +
+ +
+ +
+
+ {{form.as_p}} + + {% csrf_token %} +
+
+ + + +{% endblock %} diff --git a/call/templates/call/statement.html b/call/templates/call/statement.html new file mode 100644 index 0000000..2cb0b95 --- /dev/null +++ b/call/templates/call/statement.html @@ -0,0 +1,57 @@ +{% load static %} +{% block extrahead %} + +{% endblock %} + +{% block content %} + + + + + + + + + +
+ + +

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.

+
+

Application deadlines:

+

+ March 4, 2024 (non-EU + EU)
+ May 8, 2024 (EU)
+

+

Open days:

+

+ February 6, 2024 (online)
+ February 15, 2025 (IRL)
+

+

+
+ +

+
+ +

CSS Speech bubbles from Jos Faber.

+ + + + +{% endblock %} diff --git a/call/templates/call/walk.html b/call/templates/call/walk.html new file mode 100644 index 0000000..afeebab --- /dev/null +++ b/call/templates/call/walk.html @@ -0,0 +1,14 @@ +{% extends "call/base.html" %} +{% load static %} +{% block extrahead %} + + + +{% endblock %} + +{% block content %} +{{object}} {{object.image}} {{object.image.url}} +
+ + +{% endblock %} diff --git a/call/tests.py b/call/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/call/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/call/urls.py b/call/urls.py new file mode 100644 index 0000000..f66a895 --- /dev/null +++ b/call/urls.py @@ -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//", CreatureDetail.as_view(), name="creature_detail"), + path("creatures//s/", Statement.as_view(), name="statement"), + path("creatures//w/", Walk.as_view(), name="walk"), + path('', views.index) +] diff --git a/call/views.py b/call/views.py new file mode 100644 index 0000000..97d0435 --- /dev/null +++ b/call/views.py @@ -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" diff --git a/call2025/__init__.py b/call2025/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/call2025/asgi.py b/call2025/asgi.py new file mode 100644 index 0000000..10e02d3 --- /dev/null +++ b/call2025/asgi.py @@ -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() diff --git a/call2025/settings.deploy.py b/call2025/settings.deploy.py new file mode 100644 index 0000000..859466c --- /dev/null +++ b/call2025/settings.deploy.py @@ -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/' \ No newline at end of file diff --git a/call2025/settings.py b/call2025/settings.py new file mode 100644 index 0000000..6539e7a --- /dev/null +++ b/call2025/settings.py @@ -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/' diff --git a/call2025/urls.py b/call2025/urls.py new file mode 100644 index 0000000..a0786ee --- /dev/null +++ b/call2025/urls.py @@ -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) + diff --git a/call2025/wsgi.py b/call2025/wsgi.py new file mode 100644 index 0000000..ebe3db1 --- /dev/null +++ b/call2025/wsgi.py @@ -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() diff --git a/graphics/corner.png b/graphics/corner.png new file mode 100644 index 0000000..0770538 Binary files /dev/null and b/graphics/corner.png differ diff --git a/graphics/corner.svg b/graphics/corner.svg new file mode 100644 index 0000000..b520278 --- /dev/null +++ b/graphics/corner.svg @@ -0,0 +1,50 @@ + + + + + + + + + + diff --git a/manage.py b/manage.py new file mode 100755 index 0000000..62d47f3 --- /dev/null +++ b/manage.py @@ -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()