Porting from jupiter lab 😨

master
km0 2 years ago
commit ad06b349dc

@ -0,0 +1,60 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": 4,
"id": "5fbf751c-841f-4089-8ef0-5267356a069b",
"metadata": {},
"outputs": [],
"source": [
"from PIL import Image\n",
"import os\n",
"UPLOAD_FOLDER = os.path.join(os.getcwd(), 'static/uploads/annotation-compass/')\n",
"\n",
"def thumbnail(image):\n",
" try:\n",
" img = Image.open(image)\n",
" img.thumbnail((128,128))\n",
" name = image.rsplit('.', 1)[0]\n",
" ext = image.rsplit('.', 1)[1]\n",
" img.save(f'{name}_thumb.{ext}')\n",
" except IOError:\n",
" pass\n",
" \n",
"files = os.listdir(UPLOAD_FOLDER)\n",
"\n",
"for file in files:\n",
" thumbnail(os.path.join(UPLOAD_FOLDER, file))"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "b65ddab7-a74d-4c32-8f70-ab9e21388a55",
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.7.3"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

@ -0,0 +1,10 @@
break_messages = [
"sorry I fell asleep come back in 10 min",
"Sorry I'm not available at the moment",
"sorry, i went to the toilet, see u back in 5 min",
"ehm, refresh the page in 3 min",
"ah i went on a break, you should go on a break too"
"Ah! you are here! I wasn't excepting of you! Wait to put something on me"
"I wasn't ready for this connection. I am a bit moody right now, so I am concentrating on finding my center."
"Upss I am having a break right now, cuddling my cat."
]

@ -0,0 +1,126 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": 88,
"id": "e7bd7834-0370-4685-ae24-cd7faa6e08f9",
"metadata": {},
"outputs": [],
"source": [
"from error_messages import break_messages\n",
"import random, datetime, time\n",
"from datetime import date\n",
"import pytz"
]
},
{
"cell_type": "code",
"execution_count": 89,
"id": "74347109-d30a-476a-b903-cd3fa5b9274a",
"metadata": {},
"outputs": [],
"source": [
"def random_line(txt_list):\n",
" message= choice(txt_list)\n",
" print (message)"
]
},
{
"cell_type": "code",
"execution_count": 90,
"id": "8d527146-7473-4f02-b23e-61057a15a770",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"sorry I fell asleep come back in 10 min\n"
]
}
],
"source": [
"random_line(break_messages)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "5d303ab1-47c1-4467-858b-7d84dc257e36",
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "code",
"execution_count": 91,
"id": "8ba06a30-a28d-49cf-84d6-af1416945261",
"metadata": {},
"outputs": [],
"source": [
"today = date.today()\n",
"altro = ('2021-12-15')"
]
},
{
"cell_type": "code",
"execution_count": 92,
"id": "07738cdb-46cb-46ba-bf7e-2ed8813a3ff7",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"2021-12-15\n"
]
}
],
"source": [
"if today != altro:\n",
" print(altro)"
]
},
{
"cell_type": "code",
"execution_count": 93,
"id": "7b28edc8-8762-4834-84b5-b7ca913902c3",
"metadata": {},
"outputs": [],
"source": [
"tz = pytz.timezone('Europe/Berlin')\n",
"moment = datetime.datetime.now(tz)\n",
"timestamp = moment.__str__()\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "aaea2507-5bd0-4930-bf78-ed3ad9622276",
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.7.3"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

@ -0,0 +1,145 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"id": "04ca94d4-7339-4e44-aa28-03e77fa534f8",
"metadata": {},
"outputs": [],
"source": [
"import importlib \n",
"ac = importlib.import_module(\"projects.annotation-compass.annotation_compass\")"
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "7dd00f4d-28a0-4fd4-9cc9-17d8cbb61023",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"<module 'projects.annotation-compass.annotation_compass' from '/var/www/html/si16-app/projects/annotation-compass/annotation_compass.py'>\n"
]
}
],
"source": [
"print(ac)"
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "2aafb83a-b857-4ce7-af76-cb24c24d6f09",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"{'response': 'ok',\n",
" 'labels': [{'position': {'x': 71.4815, 'y': 13.4748},\n",
" 'size': {'width': 51.6667, 'height': 70.9862},\n",
" 'text': 'love after love\\n\\nthe time will come\\nthat with ecstacy\\nyou will welcome your self\\nas you will reach at your own door, at your own mirror,\\nand the one while smiling will welcome the other\\n\\nand stay here they would say. eat.\\nyou will love again the stranger that your self was.\\ngive some wine. give some bread. give back your heart.\\nto herself, to the stranger that loved you\\n\\nall your life, that you avoided\\nfor someone else, that had mirrored you.\\nput down the love poems from the shelf,\\n\\nthe photos, the desperate notes,\\ntear down your image from the mirror.\\nsit down. enjoy your life.',\n",
" 'timestamp': datetime.datetime(2021, 12, 15, 10, 57, 24),\n",
" 'userID': '8990638769',\n",
" 'image': 'greek.jpg'},\n",
" {'position': {'x': 76.5434, 'y': -0.71048},\n",
" 'size': {'width': 7.74749, 'height': 87.9218},\n",
" 'text': 'μ\\nι\\nτ\\n σ\\nα\\n\\n\\nγ\\nι\\nρ\\nν π\\nα η\\n σ\\n ο\\n\\n\\nι\\n\\n \\nεσ\\n τ\\n ο\\n\\nτ\\n υ\\nλ \\n ε\\nφ\\nο\\n ν\\nα\\n\\n ',\n",
" 'timestamp': datetime.datetime(2021, 12, 17, 12, 56, 4),\n",
" 'userID': '7152574796',\n",
" 'image': 'greek.jpg'},\n",
" {'position': {'x': 83.1432, 'y': 4.08526},\n",
" 'size': {'width': 28.264, 'height': 87.7442},\n",
" 'text': \"Μ\\n Ι\\nΤ\\n Σ\\nΑ\\n\\n\\n Γ Ι\\n Ρ\\n Ν \\n Α\\n\\n\\nΠ \\n Ι\\nΣ\\nΟ\\n\\n\\n\\nΙ'\\n\\n\\n\\n Ε\\nΣ\\nΤ\\n Ο\\n\\n\\n\\n Τ \\nΥ\\n Λ Ε\\n ΦΟ\\nΝ\\n Α\\n\\n\\n\\n Φ\\n\\n \",\n",
" 'timestamp': datetime.datetime(2021, 12, 17, 12, 59, 43),\n",
" 'userID': '7152574796',\n",
" 'image': 'greek.jpg'}]}"
]
},
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"ac.get_labels_url('greek.jpg')"
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "df20cbd0-e837-4a01-b501-206b8a884c1c",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"/var/www/html/si16-app/projects/annotation-compass/descriptions.json\n"
]
},
{
"data": {
"text/plain": [
"\"Welcome to many to one translation!\\r\\n\\r\\nI would like you to translate this poem to english. You can use a translator to obtain the translation of single words, but i hope you will do your best to make a thoughtful choice on the word you will choose. Every word has its own power!\\r\\n\\r\\nThis collection of translations will be part of the material I will use to experiment with my mashup() function, that can be used only with texts with the same number of lines. Therefore, I kindly ask you to create the spaces next to the text, in order, and one space per line. This is so that the content extracted will be ready to be processed.\\r\\n\\r\\n .b Click and drag to create the space where you want to insert your translation (line by line, in order, one space per line)\\r\\n .c Click ''insert'' or click on the ''x'' if it's not as you want\\r\\n\\r\\nThank you for your participation!\\r\\nGet back on our website soon to see how your contribution has been used.\""
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"ac.get_image_description('greek.jpg')"
]
},
{
"cell_type": "code",
"execution_count": 7,
"id": "c012b7fe-6de7-4ce2-91df-ebc544006bd4",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"/var/www/html/si16-app\n"
]
}
],
"source": []
},
{
"cell_type": "code",
"execution_count": null,
"id": "63f6fd83-173a-47c5-87b4-5353a7b4e210",
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.7.3"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

@ -0,0 +1,317 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"id": "aecab081-a69a-4e38-9a30-cfc95661e56b",
"metadata": {},
"outputs": [],
"source": [
"from urllib.request import urlopen\n",
"import json"
]
},
{
"cell_type": "markdown",
"id": "9538705e-e51e-49da-b4fe-e6c4c6f6c435",
"metadata": {},
"source": [
"Area Map:\n",
"Give a string with the name of the image-file that was annotated with the Annotation Compass; Select a specific area of the image, return a list of all labels in that specific area."
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "13246f4a-81ac-4b68-b974-ae04064d10cc",
"metadata": {},
"outputs": [],
"source": [
"# function for the project!\n",
"# please, kamo, put it in the flask app! thxxxx\n",
"# (combined with html_tag_list)\n",
"\n",
"url = \"https://hub.xpub.nl/soupboat/generic-labels/get-labels/?image=rejection_map.jpg/\"\n",
"response = urlopen(url)\n",
"data_json = json.loads(response.read()) \n",
"\n",
"def area_map_list(labels: list, left: int, right: int, top: int, bottom: int ) -> list: \n",
" \n",
" filtered_map = []\n",
" for label in labels:\n",
" if left <= (label['position']['x']) <= right and top <= (label['position']['y']) <= bottom:\n",
" filtered_map.append(label)\n",
" \n",
" return filtered_map\n",
"\n",
"#area_map_list(data_json['labels'], 0, 10, 30, 70)"
]
},
{
"cell_type": "markdown",
"id": "34b43aae-0830-47ca-86c9-5827bfa44cef",
"metadata": {},
"source": [
"Ghost Map:\n",
"Give a string with the name of the image-file that was annotated with the Annotation Compass; Replace all characters of all annotation-texts with a ghost-glyph and return a string that includes all replaced annotation-texts plus html-tags that place them back into their original position."
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "dcbe893c-d00a-4933-9a18-8216a5f7459a",
"metadata": {},
"outputs": [],
"source": [
"# function for the project!\n",
"# please, kamo, put it in the flask app! thxxxx\n",
"# this function links to jian.css\n",
"\n",
"url = \"https://hub.xpub.nl/soupboat/generic-labels/get-labels/?image=rejection_map.jpg/\"\n",
"response = urlopen(url)\n",
"data_json = json.loads(response.read()) \n",
"\n",
"def ghost_map_list(labels: list, ghost_glyph: str ) -> str:\n",
" \n",
" filtered_map = '<link rel=\"stylesheet\" href=\"/soupboat/si16-app/static/css/jian.css\">'\n",
" for label in labels:\n",
" replaced_text = ''\n",
" for char in label['text']:\n",
" replaced_char = char\n",
" if not char.isspace():\n",
" replaced_char = ghost_glyph\n",
" replaced_text = replaced_text + replaced_char\n",
" html_tag = f'<p class=\"ghost\" style=\"left: {label[\"position\"][\"x\"]}%; top: {label[\"position\"][\"y\"]}%; position: absolute;\">{ replaced_text }</p>'\n",
" filtered_map = filtered_map + html_tag\n",
" return filtered_map\n",
"\n",
"#ghost_map_list(data_json['labels'], '.')"
]
},
{
"cell_type": "markdown",
"id": "863a6716-d9e4-413f-a597-a16cc0f04d6d",
"metadata": {},
"source": [
"Highlight Map:\n",
"Give a string with the name of the image-file that was annotated with the Annotation Compass; Give a target-word; Return a string that includes all annotation-texts plus html-tags that place them back into their original position while highlighting the annotation-texts that include the target."
]
},
{
"cell_type": "code",
"execution_count": 5,
"id": "18cfc4fa-4039-49fe-8b29-5bbec0e89b6f",
"metadata": {},
"outputs": [],
"source": [
"# function for the project!\n",
"# please, kamo, put it in the flask app! thxxxx\n",
"# this function links to jian.css\n",
"\n",
"url = \"https://hub.xpub.nl/soupboat/generic-labels/get-labels/?image=rejection_map.jpg/\"\n",
"response = urlopen(url)\n",
"data_json = json.loads(response.read()) \n",
"\n",
"def highlight_map_list(labels: list, target: str ) -> str:\n",
"\n",
" filtered_map = '<link rel=\"stylesheet\" href=\"/soupboat/si16-app/static/css/jian.css\">'\n",
" for label in labels:\n",
" if target in label['text']:\n",
" highlight_tag = f'<p class=\"highlight\" style=\"left: {label[\"position\"][\"x\"]}%; top: {label[\"position\"][\"y\"]}%; position: absolute;\">{ label[\"text\"] }</p>'\n",
" filtered_map = filtered_map + highlight_tag\n",
" else:\n",
" html_tag = f'<p class=\"lowlight\" style=\"left: {label[\"position\"][\"x\"]}%; top: {label[\"position\"][\"y\"]}%; position: absolute;\">{ label[\"text\"] }</p>'\n",
" filtered_map = filtered_map + html_tag\n",
" \n",
" return filtered_map\n",
"\n",
"#highlight_map_list(data_json['labels'], 'tunnel')"
]
},
{
"cell_type": "markdown",
"id": "9c41f5fd-1071-43b3-88bc-41a8e5907fdb",
"metadata": {},
"source": [
"html-tag:\n",
"Give a string with the name of the image-file that was annotated with the Annotation Compass; Return a string that can include the position, text, timestamp and/or userID of all labels plus html-tags that place them back into their original position."
]
},
{
"cell_type": "code",
"execution_count": 6,
"id": "a7ee1bdf-a356-4a66-aa01-ef1dad8738ca",
"metadata": {},
"outputs": [],
"source": [
"# function for the project!\n",
"# please, kamo, put it in the flask app! thxxxx\n",
"# this function links to jian.css\n",
"\n",
"url = \"https://hub.xpub.nl/soupboat/generic-labels/get-labels/?image=rejection_map.jpg/\"\n",
"response = urlopen(url)\n",
"data_json = json.loads(response.read()) \n",
"\n",
"def html_tag_list(labels: list, position: bool, text: bool, timestamp: bool, userID: bool ) -> str: \n",
" \n",
" html_tags = '<link rel=\"stylesheet\" href=\"/soupboat/si16-app/static/css/jian.css\">'\n",
" for label in labels:\n",
" html_tags = html_tags + f'<p style=\"left: {label[\"position\"][\"x\"]}%; top: {label[\"position\"][\"y\"]}%; position: absolute;\">'\n",
" if position == True:\n",
" html_position = f'{ label[\"position\"] } '\n",
" html_tags = html_tags + html_position\n",
" if text == True:\n",
" html_text = f'{ label[\"text\"] } '\n",
" html_tags = html_tags + html_text\n",
" if timestamp == True:\n",
" html_timestamp = f'{ label[\"timestamp\"] } '\n",
" html_tags = html_tags + html_timestamp\n",
" if userID == True:\n",
" html_userID = f'{ label[\"userID\"] } '\n",
" html_tags = html_tags + html_userID\n",
" html_tags = html_tags + '</p>'\n",
" \n",
" return html_tags\n",
"\n",
"#html_tag_list(data_json['labels'], True, False, False, False)"
]
},
{
"cell_type": "markdown",
"id": "d05dd98e-0cdb-4a50-839a-7a67cdc22010",
"metadata": {},
"source": [
"Individual Map:\n",
"give a string with the name of the image-file that was annotated with the Annotation Compass; Select one or more specific targets and return a list of all labels that include these targetsselect one or more specific users and return a list of all labels from these users."
]
},
{
"cell_type": "code",
"execution_count": 9,
"id": "e8d7824d-4523-46f7-b89f-0e8f2d98e564",
"metadata": {},
"outputs": [],
"source": [
"# function for the project!\n",
"# hei kamo! is it already in the flask? should be?\n",
"# (combined with html_tag_list)\n",
"\n",
"url = \"https://hub.xpub.nl/soupboat/generic-labels/get-labels/?image=rejection_map.jpg/\"\n",
"response = urlopen(url)\n",
"data_json = json.loads(response.read()) \n",
"\n",
"def individual_map_list(labels: list, users: list ) -> list: \n",
" \n",
" filtered_map = []\n",
" for label in labels:\n",
" for user in users:\n",
" if label['userID'] == user:\n",
" filtered_map.append(label)\n",
" return filtered_map\n",
"\n",
"#individual_map_list(data_json['labels'], ['5058763759', '5941298752'])"
]
},
{
"cell_type": "markdown",
"id": "0f3febed-e702-4585-a7a1-e62d1a16d1c0",
"metadata": {},
"source": [
"Target Map:\n",
"Give a string with the name of the image-file that was annotated with the Annotation Compass; Select one or more specific targets and return a list of all labels that include these targets"
]
},
{
"cell_type": "code",
"execution_count": 7,
"id": "9d263750-bab0-4989-9074-a58eba21b2c5",
"metadata": {},
"outputs": [],
"source": [
"# function for the project!\n",
"# please, kamo, put it in the flask app! thxxxx\n",
"# (combined with html_tag_list)\n",
"\n",
"url = \"https://hub.xpub.nl/soupboat/generic-labels/get-labels/?image=rejection_map.jpg/\"\n",
"response = urlopen(url)\n",
"data_json = json.loads(response.read()) \n",
"\n",
"def target_map_list(labels: list, targets: list ) -> list: \n",
" \n",
" filter_map = []\n",
" for label in labels:\n",
" for target in targets:\n",
" if target in label['text']:\n",
" filter_map.append(label)\n",
" return filter_map\n",
"\n",
"#target_map_list(data_json['labels'], ['tunnel', 'happy'])"
]
},
{
"cell_type": "markdown",
"id": "a8e5a256-b9e6-4f0f-b395-690c36d88a7e",
"metadata": {},
"source": [
"Vernacular Map:\n",
"Give a string with the name of the image-file that was annotated with the Annotation Compass; Return a string that includes all annotation-texts plus html-tags that place them back into their original position."
]
},
{
"cell_type": "code",
"execution_count": 8,
"id": "d78dca69-5f1e-408d-85b5-6cc594f2f875",
"metadata": {},
"outputs": [],
"source": [
"# function for the project!\n",
"# please, kamo, put it in the flask app! thxxxx\n",
"# this function links to jian.css\n",
"\n",
"url = \"https://hub.xpub.nl/soupboat/generic-labels/get-labels/?image=rejection_map.jpg/\"\n",
"response = urlopen(url)\n",
"data_json = json.loads(response.read()) \n",
"\n",
"def vernacular_map_list(labels: list) -> str: \n",
"\n",
" filtered_map = '<link rel=\"stylesheet\" href=\"/soupboat/si16-app/static/css/jian.css\">'\n",
" for label in labels:\n",
" html_tag = f'<p style=\"left: {label[\"position\"][\"x\"]}%; top: {label[\"position\"][\"y\"]}%; position: absolute;\">{ label[\"text\"] }</p>'\n",
" filtered_map = filtered_map + html_tag\n",
" \n",
" return filtered_map\n",
"\n",
"#vernacular_map_list(data_json['labels'])"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "6a207852-701e-48ef-8469-96c245aea6a4",
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.7.3"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

@ -0,0 +1,87 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"id": "1d499616-2ed6-49c5-a3f3-394f5330dd1f",
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "code",
"execution_count": null,
"id": "96517076-dff6-4e43-b858-0215eb5e538e",
"metadata": {},
"outputs": [],
"source": [
"# --------ETC PORTAL------------ #\n",
"\n",
"@app.route(f'/{base_url}/api/')\n",
"https://hub.xpub.nl/soupboat/~chae/api/\n",
"\n",
"@app.route(f'/{base_url}/the-etc-portal-is-open/')\n",
"https://hub.xpub.nl/soupboat/~chae/api/the-etc-portal-is-open/\n",
"\n",
"@app.route(f'/{base_url}/start-contaminating/', methods = ['GET'])\n",
"https://hub.xpub.nl/soupboat/~chae/api/start-contaminating/\n",
"\n",
"@app.route(f'/{base_url}/the-etc-portal-is-never-opened/', methods = ['GET', 'POST'])\n",
"https://hub.xpub.nl/soupboat/~chae/api/the-etc-portal-is-never-opened/ \n",
"\n",
"@app.route(f'/{base_url}/the-etc-portal-is-closed/', methods = ['GET', 'POST'])\n",
"https://hub.xpub.nl/soupboat/~chae/api/the-etc-portal-is-closed/\n",
"\n",
"\n",
"# --------AND I WISH THAT YOUR QUESTION HAS BEEN ANSWERED------------ #\n",
"\n",
"@app.route(\"/{base_url}/and_i_wish_that_your_question_has_been_answered/\", methods=['GET', 'POST'])\n",
"https://hub.xpub.nl/soupboat/~grgr/api/and_i_wish_that_your_question_has_been_answered/\n",
"\n",
"@app.route(f\"/{base_url}/archive/\", methods=['GET'])\n",
"https://hub.xpub.nl/soupboat/~grgr/api/archive/\n",
"\n",
"@app.route(f\"/{base_url}/save/\", methods=['GET', 'POST'])\n",
"https://hub.xpub.nl/soupboat/~grgr/api/save/\n",
"\n",
"\n",
"# --------VERNACULAR MAPS------------ #\n",
"\n",
"@app.route(f\"/{base_url}/annotation-compass/\", methods=['GET', 'POST'])\n",
"\n",
"@app.route(f\"/{base_url}/annotation-compass/annotate/<image>/\", methods=['GET', 'POST'])\n",
"\n",
"@app.route(f\"/{base_url}/annotation-compass/get-labels/<image>\", methods=['GET', 'POST'])"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "56987d98-84ba-43c9-88a9-fc365cca2ecc",
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.7.3"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

File diff suppressed because one or more lines are too long

@ -0,0 +1,34 @@
---
title: About
description: Information About the SI16 - Learning How To Walk While Catwalking
url:
---
### INFO
Learning How to Walk While Catwalking, XPUB, 2021
XPUB (the Experimental Publishing master from Piet Zwart Institute, Willem de Kooning Academy) welcomes you to the Special Issue 16 on vernacular language processing: Learning How to Walk while Catwalking.
Our Special Issue is a toolkit to mess around with language: from its standard taxonomies and tags, to its modes of organizing information and its shaping knowledge. With these tools we want to legitimize failures and amatorial practices by proposing a more vernacular understanding of language.
We decided to release the Special Issue 16 toolkit in the form of an API (Application Programming Interface). APIs organise and serve data on the Internet; what is not always evident is that they facilitate exchange of information following mainly commercial purposes. However, Our API is an attempt at a more critical and vernacular approach to such model of distribution. You didn't get a thing yet? Don't worry! We are also on our way and that's the whole point of this experimental enquiry. We will be happy to guide you through the API and the different functions included in it, share our technical struggles and findings.
The overall project as mentioned above is a toolkit, thus hosts multiple tools. The SI16 website is a vessel for you to navigate from one to another of those projects smoothly. Where you will enter a sea of possibilities.
This project is characterized by the elaboration of functions, at the heart of our tools, that consider and process vernacular languages. The material we process comes from various sources. For some of it, we appropriated existing texts and compiled them into corpora. Alternatively, the activation of certain functions calls for an audiences input. The participatory aspect of the functions is an important factor that unites them.
We approach the text as a texture, a malleable clay tablet, a space for foreign input and extensive modifications, for cut-up and for collage, for collective agency and participation. Not a surface but a volume, in which the text is not only text, but a shared space. We work to sort out several meanings from the same text. We intend to blur our roles as authors, users and public because this is an act of collective world building.
The Special Issue 16's title "Learning How To Walk While Catwalking" is a playful mention to illustrate how we experienced the present project and the mentality in which we want you to engage with it: be confident, be ambitious and be ready to fail a lot.
### COLOPHON
All the code and the contents produced through this API are automatically licensed under the Catleft Licence. (url Licence?)
Gersande Schellinx, Mitsa (Dimitra Chaida), Erica Gargaglione, Francesco Luzzana, Chaeyoung Kim, Emma Prato, Miriam Schöb, Supisara Burapachaisri, Jian Haake, Ål Nik (Alexandra Nikolova), Kimberley Cosmilla, Carmen Gray
Manetta Michael Cristina Steve Clara Danny Aymeric Simon Etc
Pirelli is a nice typeface from Jung-Lee Type Foundry (J-LTF)

@ -0,0 +1,11 @@
---
title: Corpora
description: Information About the SI16's Corpora
url:
---
## INFO
The functions from the Special Issue 16 were built to experiment and play around with vernacular language processing. Before a tool can replace, contaminate, reverse or mashup, it needs textual material as an input.
Here we gather corpora, small collections of linguistic data that were inspiring and useful in our research, experiments or projects.

@ -0,0 +1,17 @@
---
title: Functions
description: Information About the SI16's Functions
url:
---
## INFO
The functions from the Special Issue 16 have been freshly delivered into this world for you. Our first steps into the realm of Language Processing, our first attempts at Vernacular Language Processing.
What we share with you here are stripped-out functions we made use of in our tools. Some might still need more context than others, but essentially they can be used in different fashions depending of the users' wants and needs.
We wanted you, whoever you are: friend or online scroller, beloved internet user or binge watcher, even IRL human being … whoever you are! We wanted us to share with you how our tools came to life, expose the internal mechanism to the text processing you are about to engage with.
You didn't quite get it? Basically, our project is meant to give a bunch of users several tools such as : ✂️ scissors, 📃 sticky notes, ✏️ pencils, erasers, and printed paper. ✂️🖊📝✏️📃 And let them have fun. Cutting it and putting it together, making notes and writing jokes … But everything in a digital format.
Have a look around and have fun!

@ -0,0 +1,109 @@
---
title: Intro
description: Manifesto with Vernacular Excitement
css: intro.css
---
<script src="/soupboat/si16-app/static/js/bubbles.js" defer></script>
<p class="intro">
Dear friend and online scroller, <br>
Beloved internet user, <br>
Dearest binge watcher and human being IRL,
</p>
XPUB1 (The Experimental Publishing Master from Piet Zwart Institute) welcomes you to the Special Issue 16 on vernacular language processing: "Learning How to Walk while Catwalking."
<p class="bubble">
Hu? How do you learn how to walk while catwalking?
</p>
Be confident, be ambitious and be ready to fail a lot.
Our Special Issue is a toolkit to mess around with language: from its standard taxonomies and tags, to its modes of organizing information and its shaping knowledge. With these tools we want to legitimize failures and amatorial practices by proposing a more vernacular understanding of language.
We decided to release the Special Issue 16-toolkit in the form of an API (Application Programming Interface).
APIs often organise and serve data and knowledge.
What is not always evident is that they facilitate the exchange of information between different software programs and systems according to mainly commercial standards and purposes. We chose instead to build a process that responds to the topics we are working with.
Our API is an attempt at a more critical and vernacular approach to such model of distribution. You didn't get a thing yet? Don't worry! We are also on our way and that's the whole point of this experimental enquiry. We will be happy to guide you through the API and the different functions included in it, share our technical struggles and findings.
<p class="bubble">
This project is characterized by the elaboration of vernacular methods of processing. The material we process comes from various sources. For some of it, we appropriated existing texts and compiled them into corpora. For others, the activation of certain functions calls for an audiences input. The participatory aspect of the functions is an important factor that unites them.
</p>
Since we are working with filters, we realized how every cultural object rejects and filters its public. We want to question these limitations focusing on accessibility and proposing several entry points to our project.
<p class="bubble">
API, theres this very good meme that, I think, explains it in a rather good way. Imagine a bar with different staff in it: The cooks working in the kitchen would be the backend, the ones behind the bar the frontend, andddd the waiters running from the bar to the tables are the API!
</p>
<p class="bubble">
Something that feels informal, approachable, "ours" and not imposed standarized forms. Organic, with the spanish opening times, etc. Approachable.
</p>
<p class="bubble">
wtf I don't really understand but I like it
</p>
In other words, a toolkit for processing language with a vernacular attitude. This toolkit does not only consist of a set of tools but also of a world we are building around them: how do we want these tools to affect reality? This toolkit can be expanded, as new tools can be added to it and the world around them being stretched. There is a strong focus on the way we are working on it: a decentralized approach that builds from the ground up.
<p class="bubble">
Ambitious! Political! Unstable! ...but as some point embracing this unstability, trying to learn and care for each other, while learning python and caring for API .
</p>
<p class="bubble">
I think of it as a personification of something that's intended to be functional, in that we assign the API a particular behavior so that it does the unexpected.
</p>
<p class="bubble">
Opening times?
</p>
We are confident, we are ambitious and we are failing a lot while Learning How To Walk While Catwalking. We want to legitimize failures and amateur practices outside the hierarchy of experience. We want to care of each other in the process of learning, now between us, and then with you.
We approach the text as a texture, a malleable clay tablet, a space for foreign input and extensive modifications, for cut-up and for collage, for collective agency and participation. Not a surface but a volume, in which the text is not only text, but a shared space. We work to sort out several meanings from the same text. We intend to blur our roles as authors, users and public because this is an act of collective world building.
<p class="bubble">
I was invited to get onto an online platform for something called the Special Issue 16(?) The front, or first page has an index with some descriptions. Overall it had to do with texts, and ways of modifying them, I think.
</p>
<p class="bubble">
Landing on Special Issue 16 page, reading the 'about' page. Finding out about several projects, triggered by the different showcases. As I gain interest in one of the example, I click on a link and read about the intention from which this tool departed.
</p>
<p class="bubble">
So well, I am in front of the screen, I click on the first link and get a description and a sort of instruction of how to use their tools. Fine, I'll use the tool. It seems like I'm not the only one who has been invited here, the layout is unfamiliar, but I see how I could partake in it. And if I do, well, the next person will also have something else to deal with, I'm into that. What could I write? I write. Oh, wasn't that hard.
</p>
<p class="bubble">
Unapologetic. Fearless. Eager. Playful. Brave. Persistent. Experimental. Bvvvrruummm
</p>
<p class="bubble">
I am curious to know even more, I click on the shared folder link and am redirected to a library of tools. Finding out I can use this tool for my own purposes. I start scrolling through the multiple tools offered there. From the tool I was initially interested on, I drift to another snippet that calls my attention. From there, I click on the link offering a 'showcase view'. I get acquainted with the example. Zooming out, I land on the 'about page' to which that project belongs. More tools are offered but I am not interested in more tools. I zoom out more, and I find the 'introduction', which informs me about the general purpose of this page.
</p>
<p class="bubble">
OMG THIS IS. HERE?
</p>
<p class="bubble">
wow these people are so meta
</p>
<p class="bubble">
can you buy me a coke?
</p>
<p class="bubble">
I clicked on the link to access this website: oh what happens nice! nice colours, I have icons to click on but I clicked on one and I've seen many things i don't understand so it's better to know what this is about first because I don't understand so I click on the about page.
Ok let's move back to the homepage. I can choose between projects and functions (again, what's this????) ok maybe by looking at the projects I will understand better...
</p>
<p class="bubble">
Embracing the chaos that comes with the learning curve.
</p>
<p class="bubble">
I click on a link and am brought to a page and keep clicking, can't stop clicking, super curious what's happening here, not sure what exactly yet, but it doesn't matter. I see the about page but I'd rather not read it because I like surprises. Looks like they're making a lot of experimental tools I've never heard of before but always wanted to try.
</p>
<p class="bubble">
What is this all about? Shall we open the window for some fresh air?.
</p>
Yes that was a bit conceptual but basically, our project is meant to give a bunch of users several tools such as : ✂️ scissors, 📃 sticky notes, ✏️ pencils, erasers, and printed paper. ✂️🖊📝✏️📃 And let them have fun. Cutting it and putting it together, making notes and writing jokes… But everything in a digital format.
Q&A link link link

@ -0,0 +1,16 @@
---
title: License
description: Information About the SI16's License
url:
---
------------------------------------------Catleft License--------------------------------------------
The Catleft license wants to legitimise failures, promote critical approaches, and embrace the user as co-author.
Conditions for reuse:
1. Any reproduction or redistribution of the material under this license has to be readable by both humans and machines.
2. It is adviced to use, reproduce, modify and redistribute the material according to the user's own language and dialect.
3. The material can be reproduced, distributed, or transmitted in any form, by any means, and any purpose except for commercial purposes. In this case, the material can only be reused with the written permission of one of the license holders. For permission requests, write to XPUB department.
4. Always include this license with the distribution of your project or specify where it can be found it.
Make a copy of the text of the license and paste it with the date and name of the license holder. Put this license file in the root directory of your project repository and include it with the distribution of your project.

@ -0,0 +1,11 @@
---
title: Projects
description: Information About the SI16's Projects
url:
---
## INFO
The projects from the Special Issue 16 have accompanied us on our way to build this toolkit. Whereas our practice shaped the functions we created, the tools formed our projects to a similar extent.
Each of them is an experimental adventure, elaborating on different aspects of vernacular language processing while inviting users to participate. Come with us to see how our functions come to life and join us in this fearless and joyful undertaking.

@ -0,0 +1,9 @@
---
title: Learning How to Walk While Catwalking
description: Information About the SI16 - Learning How To Walk While Catwalking
cover: url to an image in the static folder we can use for social etc Recommend 1200×628 no much text
url: https://hub.xpub.nl/soupboat/si16-app/
license: url to the license we need to upload it on the git repo?
---
## INFO

@ -0,0 +1,10 @@
<form action='https://hub.xpub.nl/soupboat/si16-app/api/cocktail_generator/'>
<input class='list-input' type="text" name='alcohol'>
<input class='list-input' type="text" name='decor'>
<input type="submit">
</form>
<script src="/soupboat/si16-app/static/js/inputList.js" defer ></script>

@ -0,0 +1,38 @@
---
title: Terms of Service
description: Terms of Service SI16
css: tos.css
---
## Vernacular catwalking, learning and teaching.
Our API (Application Programming Interface) is in itself an experiment to legitimise failures, promote a more critical approach to APIs as models of distribution, and embrace the user as co-author. It is accessible both in html and json format. The aim of this ToS is to articulate inclusive conditions for vernacular catwalking, learning and teaching.
### 0. Access
This service can be used by any individual persons, organizations, institutions or collectives that are willing to reject any sort of stigma towards languange, ignore grammar correctness and officiality, both in oral and written language, and who are interested in vernacular amatorial practices around and within language processing and the use that is made of it.
### 1. By using this service, you agree on the following:
1. All the code and the contents produced through this API are automatically licensed under the _catleft license_
2. the API key comes in the form of a physical keychain and shall not be distributed through any other media different than the physical keychain itself.
3. The API key is held by a rotating "guardian" who:
1. shall leave a contact on the page _____ in order to be reached.
2. is responsible of adding the new API key to the keychain if modified.
3. is open to and seeks for collective consultation about the changes made to the API
4. The API key can be used for max. 10 days. After whose expiration it must be passed onto another guardian.
5. no personal data is collected, except for the contact of the guardian of the API-key
### 2. Privileges granted by the key
1. it allows to modify the API key; in this case the new API key must be added, in any physical form, as keychain gadget to the API key.
2. It allows to add new error messages, which shall include messages of self-care, encouragement, anecdotes about cats, common saying in any dialect, and/or favourite recipes.
3. it allows to access the complete hidden reading list and to add new suggestions.
Note: we reserve the right to modify or delete contributions that are homophobic, transphobic, racist, ableist and sexist, even if intended as a joke, or as an ironic remark. Same for hate speech, such as, but not limited to: white supremacy, ethnostate advocacy, discussion of national socialism / nazism.
### 3. How to pass on the key
The current guardian of the API-key shall pass the API-key to another person of their choice at the expiring of the 10 days.
### 4. Contacts

@ -0,0 +1,123 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "e839d993-7f9e-4419-92de-eb3dba046640",
"metadata": {},
"source": [
"# add_pen_name"
]
},
{
"cell_type": "markdown",
"id": "b87b3999-97c7-43da-9931-27f319a9f590",
"metadata": {},
"source": [
"___\n",
"\n",
"Add pen names into the list<br> \n",
"\n",
"<img src=\"https://hub.xpub.nl/soupboat/~chae/break-it-down.jpeg\">"
]
},
{
"cell_type": "markdown",
"id": "cf3788e7-7b88-480a-8a4e-8c53eae169e3",
"metadata": {
"tags": []
},
"source": [
"Input:\n",
"- name, string"
]
},
{
"cell_type": "markdown",
"id": "96ccfe4d-a20d-42e6-ad2c-fdfaa6fd2fc5",
"metadata": {
"tags": []
},
"source": [
"Output:\n",
"- name, list"
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "4a5f59ac-bdfd-4695-8842-80db12bd156e",
"metadata": {},
"outputs": [],
"source": [
"pen_name_list = ['Blibli', 'Chae as Che Gueverq', 'turtle teller']\n",
"\n",
"def add_pen_name(name_input):\n",
" '''add pen name inside the list'''\n",
" if name_input:\n",
" if name_input not in pen_name_list:\n",
" pen_name_list.append(name_input)\n",
" return pen_name_list"
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "a9668cd3-54a4-4e1b-baed-f8453965f693",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"['Blibli', 'Chae as Che Gueverq', 'turtle teller', 'broggle']"
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# simple application here\n",
"add_pen_name('broggle')"
]
},
{
"cell_type": "markdown",
"id": "5db35904-9350-428d-9744-a922ebd05784",
"metadata": {},
"source": [
"___\n",
"This function is part of C and G project, ***ETC Portal to Contaminate*** "
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "919fd920-f52f-4912-b4e2-d5aa57c5f400",
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.7.3"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

@ -0,0 +1,160 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "66f72396-0cf1-41cf-a486-2930d2ad1652",
"metadata": {},
"source": [
"# area_map\n",
"Give a string with the name of the image-file that was annotated with the Annotation Compass; Select a specific area of the image, return a list of all labels in that specific area."
]
},
{
"cell_type": "code",
"execution_count": 1,
"id": "1eb5f9a2-a86a-45f2-aa7b-9b32653f9fed",
"metadata": {},
"outputs": [],
"source": [
"from urllib.request import urlopen\n",
"import json"
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "5fb4d7db-9d58-4921-81e9-9e5fd00c4d46",
"metadata": {},
"outputs": [],
"source": [
"def area_map(image: str, left: int, right: int, top: int, bottom: int ) -> list:\n",
" \n",
" \"\"\"Give a string with the name of the image-file that was annotated with the Annotation Compass; Select a specific area of the image, return a list of all labels in that specific area.\"\"\" \n",
"\n",
" url = f\"https://hub.xpub.nl/soupboat/generic-labels/get-labels/?image={image}\"\n",
" response = urlopen(url)\n",
" data_json = json.loads(response.read()) \n",
" \n",
" filtered_map = []\n",
" for label in data_json['labels']:\n",
" if left <= (label['position']['x']) <= right and top <= (label['position']['y']) <= bottom:\n",
" filtered_map.append(label)\n",
" \n",
" return filtered_map"
]
},
{
"cell_type": "markdown",
"id": "1094ed8e-1387-481e-b612-9a8cc81d5c18",
"metadata": {},
"source": [
"This function was built for a project where individuals are invited to add their annotations on a map using the Annotation Compass. Each annotation-label is stored in a json-file and includes the annotation-text itself, but also the name of the image-file as well as the position, size, index, timestamp and userID of the annotation.\n",
"\n",
" Example for a label:\n",
"\n",
" {'image': 'map.jpg',\n",
" 'position': {'x': 12, 'y': 97},\n",
" 'size': {'width': 43, 'height': 18},\n",
" 'text': 'This is a text! Is this a text?',\n",
" 'timestamp': 'Wed, 01 Dec 2021 14:04:00 GMT',\n",
" 'userID': 5766039063}\n",
"\n",
"\n",
"If interested in the annotations in a specific area of the map, area_map() can help. The function needs a string with the name of the of the image-file that was annotated with the annotation compass tool as well as four specific values to define the left, right, top and bottom outlines of the selected area. The output is a list of all labels in the defined area of interest.\n",
"\n",
"How to get a json-file with annotation-labels?\n",
"\n",
" https://hub.xpub.nl/soupboat/generic-labels/\n",
"The Annotation Compass allows people to uplaod an image and ask others to annotate it. A json-file of the annotations is provided."
]
},
{
"cell_type": "markdown",
"id": "2c3e992a-317b-4433-8c3b-9efcedea575d",
"metadata": {
"tags": []
},
"source": [
"## Examples\n",
"\n",
"In this example, the function returns all annotation-labels in an area of interest that reaches from x = 70% to x = 90% and from y = 10% to y = 100%"
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "e3d7e97b-0089-4c29-a2c1-9ef823ff2e37",
"metadata": {
"scrolled": true,
"tags": []
},
"outputs": [
{
"data": {
"text/plain": [
"[{'image': 'rejection_map.jpg',\n",
" 'position': {'x': 76.25, 'y': 69.5246},\n",
" 'size': {'height': 12.3939, 'width': 15.625},\n",
" 'text': \"I once went to view a house here but it didn't have the floor. crap.\",\n",
" 'timestamp': 'Wed, 15 Dec 2021 11:36:51 GMT',\n",
" 'userID': '6286616941'},\n",
" {'image': 'rejection_map.jpg',\n",
" 'position': {'x': 77.0, 'y': 63.7521},\n",
" 'size': {'height': 8.65874, 'width': 10.25},\n",
" 'text': 'other housing rejection situations here.\\n',\n",
" 'timestamp': 'Wed, 15 Dec 2021 11:37:25 GMT',\n",
" 'userID': '6286616941'},\n",
" {'image': 'rejection_map.jpg',\n",
" 'position': {'x': 75.285, 'y': 29.2254},\n",
" 'size': {'height': 19.0141, 'width': 17.513},\n",
" 'text': '어딘지 잘은 모르겠지만, Kralingen 쪽이었던 것 같아. bsn 거주등록을 위해 학교에 갔는데, 시청에서 나온 사람들이 나의 룸메이트 ID card가 필요하다며 거절했지. 나의 아침을 날렸어. 나의 룸메가 나에게 ID card 사진을 보내줬지만, 그들은 서명이 같지 않다면서 다시 거절했지. ',\n",
" 'timestamp': 'Wed, 15 Dec 2021 11:39:21 GMT',\n",
" 'userID': '8633793842'},\n",
" {'image': 'rejection_map.jpg',\n",
" 'position': {'x': 73.703, 'y': 49.514},\n",
" 'size': {'height': 5.22479, 'width': 5.09839},\n",
" 'text': \"i got rejected from a skateboard here. it's normal though because it's always trying to reject you and you love it anyway\",\n",
" 'timestamp': 'Wed, 15 Dec 2021 11:44:35 GMT',\n",
" 'userID': '8918562766'}]"
]
},
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"area_map('rejection_map.jpg', 70, 90, 10, 100)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "926c7209-4a91-4401-a1a5-15066b0331bc",
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.7.3"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

@ -0,0 +1,327 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": 179,
"id": "3c7f546e-2a12-456d-be2b-a01df62c32f4",
"metadata": {},
"outputs": [],
"source": [
"image = 'think-classify.jpg'\n",
"image2 = 'think-classify2.jpg'\n",
"image3 = 'think-classify3.jpg'\n",
"image4 = 'think-classify4.jpg'\n",
"image5 = 'think-classify5.jpg'\n",
"image6 = 'think-classify6.jpg'\n",
"image7 = 'think-classify7.jpg'\n",
"image8 = 'think-classify8.jpg'\n",
"image9 = 'think-classify9.jpg'\n",
"image10 = 'think-classify10.jpg'\n",
"image11 = 'think-classify11.jpg'\n",
"image12 = 'think-classify12.jpg'\n",
"image13 = 'think-classify13.jpg'\n",
"image14 = 'think-classify14.jpg'\n"
]
},
{
"cell_type": "code",
"execution_count": 198,
"id": "42b71c63-544e-4e39-9182-f5d349eb5389",
"metadata": {
"tags": []
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[['to', 'meet,', 'but', 'also', 'to', 'collect.', 'are', 'you', 'gathering', 'people', 'or', 'a', 'bulk', 'of', 'leaves', 'in', 'your', 'garden', 'when', 'winter', 'is', 'coming?'], ['to', 'gather', 'friends', 'inside', 'your', 'place,', 'to', 'gather', 'information', 'for', 'the', 'police,', 'to', 'gather', 'food', 'for', 'the', 'homeless,', 'to', 'gather', 'objects', 'inside', 'your', 'bag,', 'to', 'gather', 'your', 'drunk', 'friend,', 'to', 'gather', 'a', 'molotof', 'cocktail', 'for', 'a', 'revolutionary', 'party'], ['it', 'is', 'the', 'opposite', 'of', 'dividing', 'because', 'the', 'basic', 'idea', 'is', 'that', 'you', 'have', 'a', 'lot', 'of', 'objects', 'far', 'away', 'from', 'each', 'other,', 'in', 'different', 'rooms,', 'in', 'different', 'building,', 'in', 'different', 'cities', 'and', 'you', 'want', 'to', 'have', 'all', 'of', 'them', 'on', 'your', 'bed', 'so', 'you', 'just', 'go', 'and', 'pick', 'them', 'up', 'from', 'where', 'they', 'are', 'you', 'put', 'them', 'in', 'a', 'bag', 'and', 'go', 'back', 'home'], ['my', 'favorite', 'activity', 'since', '200000000', 'years,', 'to', 'gather', 'food,', 'to', 'find', 'something', 'and', 'to', 'collect', 'it.', 'since', 'im', 'a', 'racoon', 'i', 'like', 'to', 'gather', 'things', 'around', 'from', 'the', 'street.', 'my', 'flatmate', 'is', 'desperate', 'about', 'it.', 'but', 'i', 'always', 'find', 'nice', 'things:', 'a', 'table,', 'a', 'confy', 'armchair,', 'some', 'baskets,', 'some', 'vases.']]\n"
]
}
],
"source": [
"from nltk.corpus import stopwords\n",
"sw = stopwords.words(\"english\")\n",
"from urllib.request import urlopen\n",
"import json\n",
"\n",
"resultSentences = []\n",
"labels_corpus = []\n",
"\n",
"url = f\"https://hub.xpub.nl/soupboat/generic-labels/get-labels/?image=think-classify7.jpg\"\n",
"response = urlopen(url)\n",
"data_json = json.loads(response.read()) \n",
"\n",
"labels = data_json['labels']\n",
"\n",
"\n",
"for label in labels:\n",
" sent = label['text'].split()\n",
" labels_corpus.append(sent)\n",
" \n",
" \n",
"print(labels_corpus)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "387f722e-bb19-45cc-a0f9-8a1186febd56",
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "code",
"execution_count": 199,
"id": "b3d40ae4-cfe8-4346-9d9f-23e2dbc50c0d",
"metadata": {
"tags": []
},
"outputs": [],
"source": [
"punctuation = ['.', ',', ';', '(', ')', ':']\n",
"\n",
"# since .split(' ') does not split a word from any punctuation, \n",
"# this function search for any string which last character (word[-1]]) is in the variable 'punctuation';\n",
"# if that is the case, the function will remove the last charachter, else it will leave it as it is.\n",
"def clean_word(word):\n",
" for character in word:\n",
" if word[-1] in punctuation:\n",
" return word[0:-1]\n",
" if word[0] in punctuation:\n",
" return word[1:]\n",
" else:\n",
" return word"
]
},
{
"cell_type": "code",
"execution_count": 200,
"id": "5c37d124-eaa8-4275-9556-473a966fdcb7",
"metadata": {},
"outputs": [],
"source": [
"# The arguments in this functions are 2 texts (text_a and text_b) an index for where the text_a starts and an index for where it ends.\n",
"def bridge(text_a, text_b, start_a, isLast):\n",
" \n",
" matchFound = 0\n",
" start_next = 0\n",
" \n",
" # for index i in text_a from a given index until the end of text_a\n",
" for i in range(start_a, len(text_a)):\n",
" if matchFound:\n",
" break\n",
" \n",
" # we name word_a the index i in text_a\n",
" word_a = text_a[i]\n",
" # if word_a is not in the given list of stopwords:\n",
" if word_a not in sw:\n",
" # for index j in the entire text_b:\n",
" for j in range(0, len(text_b)):\n",
" \n",
" # we name word_b the word with index j in text_b\n",
" word_b = text_b[j]\n",
" \n",
" # if word_a equals to word_b:\n",
" if clean_word(word_a) == clean_word(word_b):\n",
" \n",
" # resultSentences is a list to which the following informations will add up:\n",
" resultSentences.append({\n",
" 'text': text_a,\n",
" 'start': start_a,\n",
" 'end': i,\n",
" 'hasMatch': 1\n",
" })\n",
" \n",
" # if the text in position text_a is the last text to be compared:\n",
" # the same informations as above will be added, except that there will be no index for its end.\n",
" if isLast:\n",
" resultSentences.append({\n",
" 'text': text_b,\n",
" 'start': j,\n",
" 'end': None,\n",
" 'hasMatch': 1\n",
" })\n",
" \n",
" # after the match is found between the 2 texts, the function will break\n",
" matchFound = 1 \n",
" start_next = j\n",
" break\n",
" \n",
" if matchFound == 0:\n",
" resultSentences.append({\n",
" 'text': text_a,\n",
" 'start': start_a,\n",
" 'end': None,\n",
" 'hasMatch': 0\n",
" })\n",
" \n",
" if isLast:\n",
" resultSentences.append({\n",
" 'text': text_b,\n",
" 'start': 0,\n",
" 'end': None,\n",
" 'hasMatch': 0\n",
" })\n",
"\n",
" \n",
" # the function returns the index of the 'same word' in the text_b\n",
" return start_next"
]
},
{
"cell_type": "code",
"execution_count": 201,
"id": "d4862edf-c8cf-44dc-bd41-33549da33c0b",
"metadata": {},
"outputs": [],
"source": [
"def bridge_list(corpus):\n",
" start_a = 0\n",
" result = \"\"\n",
" \n",
" #for all texts indexes within the corpus to be compared:\n",
" for text_index in range(0, len(corpus)-1):\n",
"\n",
" # the last text_a to be compared has to be the text indexed as corpus[-2];\n",
" # the last text_b will then be the last text of the corpus (corpus[-1]).\n",
" isLast = text_index == len(corpus)-2\n",
" # text_a is a given index of the corpus and text_b is the following index\n",
" text_a = corpus[text_index]\n",
" text_b = corpus[text_index + 1]\n",
" \n",
" \n",
" #start_a is the index (in text_b) of the first 'common word' between text_a and text_b;\n",
" #start_a is the starting point to compare a text and its following (in index order within the corpus); \n",
"\n",
" start_next = bridge(text_a, text_b, start_a, isLast)\n",
" start_a = start_next\n",
" "
]
},
{
"cell_type": "code",
"execution_count": 202,
"id": "4363e385-7772-422b-b7b5-7f425e79878d",
"metadata": {
"tags": []
},
"outputs": [],
"source": [
"\n",
"def render_sentence(sentence, highlightNext):\n",
" result = ''\n",
" start = 0\n",
" end = len(sentence['text'])\n",
" if(sentence['start']):\n",
" start = sentence['start']\n",
" if(sentence['end']):\n",
" end = sentence['end']\n",
" \n",
" text = sentence['text']\n",
" \n",
" highlight = highlightNext\n",
" \n",
" for index in range(start, end):\n",
" word = text[index]\n",
" \n",
" if(highlight == 1):\n",
" result = result + '<span class=\"highlighit\">' + word + '</span>'\n",
" highlight = 0;\n",
" continue\n",
" else:\n",
" if index == end -1 and sentence['hasMatch']:\n",
" highlight = 1\n",
"\n",
" result = result + \" \" + word\n",
" \n",
" return result, highlight\n",
" \n",
" "
]
},
{
"cell_type": "code",
"execution_count": 203,
"id": "d30e2363-37e7-4437-af7a-572eb56d9266",
"metadata": {
"tags": []
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
" to meet, but also to collect. are you gathering people or a bulk of leaves in your garden when winter is coming? to gather friends inside your place, to gather information for the police, to gather food for the homeless, to gather <span class=\"highlighit\">objects</span> far away from each other, in different rooms, in different building, in different cities and you want to have all of them on your bed so you just go and pick them up from where they are you put them in a bag and go back home my favorite activity since 200000000 years, to gather food, to find something and to collect it. since im a racoon i like to gather things around from the street. my flatmate is desperate about it. but i always find nice things: a table, a confy armchair, some baskets, some vases.\n"
]
}
],
"source": [
"bridge_list(labels_corpus)\n",
"\n",
"endResult = ''\n",
"\n",
"highlightNext = 0\n",
"\n",
"for i in range(0, len(resultSentences)):\n",
" sentence = resultSentences[i]\n",
" start = sentence['start']\n",
" end = sentence['end']\n",
" sentenceText = sentence['text']\n",
" \n",
" sentence, highlight = render_sentence(sentence, highlightNext)\n",
" highlightNext = highlight\n",
" \n",
" endResult = endResult + \" \" + sentence\n",
"\n",
"print(endResult)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "e5e13d80-deef-423d-82f7-be71e69ee902",
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "code",
"execution_count": null,
"id": "3014a45f-d18f-4fef-b683-45eb50abed03",
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "code",
"execution_count": null,
"id": "1602a233-c977-4e8c-87f4-133f0792d181",
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.7.3"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

@ -0,0 +1,286 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "66f72396-0cf1-41cf-a486-2930d2ad1652",
"metadata": {},
"source": [
"# cocktail_generator\n",
"randomly chooses ingredients from a menu for a nice cocktail recipe"
]
},
{
"cell_type": "code",
"execution_count": 1,
"id": "9bd8860a-9967-49d5-a74a-b5de669dfe6d",
"metadata": {},
"outputs": [],
"source": [
"def cocktail_generator(alcohol: list, decor: list) -> str:\n",
" \"\"\"randomly chooses ingredients from a menu for a nice cocktail recipe\"\"\"\n",
"\n",
" import datetime\n",
" from datetime import timedelta\n",
" now = datetime.datetime.now() + timedelta(hours=1) \n",
" from random import choice\n",
"\n",
" base = [\"ORANGE JUICE\", \"PINEAPPLE JUICE\", \"APPLE JUICE\", \"MANGO JUICE\", \"FRIZZY WATER\", \"GRAPEFRUIT JUICE\", \"TONIC WATER\"]\n",
"\n",
" sour = [\"LEMON JUICE\", \"LIME JUICE\", \"PASSION FRUIT\"]\n",
"\n",
" sweet = [\"AGAVE SIRUP\", \"SUGAR SIRUP\", \"MAPLE SIRUP\"]\n",
"\n",
" omph = [\"1 SLICE GINGER\", \"2 LEAVES MINT\", \"1 SLICE CUCUMBER\", \"1 STICK CINNAMON\", \" SALT RIM\"] \n",
"\n",
"\n",
" snack = [\"CHIPS\", \"SALTED CORN\", \"PRETZELS\", \"SALTED NUTS\"]\n",
"\n",
"\n",
" XPUB1 = [\" (*(*(*(*(*.(*.*).*)*)*)*)*)*)\", \" (^(^(^(^(^.(^.^).^)^)^)^)^)^)\"]\n",
"\n",
"\n",
" drink = [\"\"\"\n",
" ___, \n",
" '._.'\\ \n",
" _____/'-.\\ \n",
" | / | \n",
" |~~~/~~| \n",
" \\ () / \n",
" '.__.' \n",
" || \n",
" _||_ \n",
" `----` \"\"\", \"\"\"\n",
" .\n",
" . . \n",
" |^ .\n",
" \\O___.____ /\n",
" \\ . /\n",
" \\ ,/\n",
" []\n",
" []\n",
" []\n",
" -------- \n",
" \"\"\", \"\"\"\n",
" \\ \n",
" .-\\\"\"\"\"\"\"\"\"-.\n",
" \\ \\__ o . /\n",
" \\/ \\ o/\n",
" \\__/. /\n",
" \\_ _/\n",
" Y\n",
" |\n",
" _.-' '-._\n",
" `---------` \"\"\"]\n",
"\n",
" \n",
"\n",
"\n",
" recipe = f\"\"\"\n",
"<pre>\n",
" \n",
"*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*\n",
"*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*\n",
"* *\n",
"* CATWALKING WITH ALCOHOL *\n",
"* *\n",
"* {now.strftime(\"%Y-%m-%d %H:%M:%S\")} *\n",
"* *\n",
"*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*\n",
"*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*\n",
"\n",
"\n",
"\n",
"2 oz {choice(alcohol)}\n",
"\n",
"150 ml {choice(base)}\n",
"\n",
"1 oz {choice(sour)}\n",
"\n",
"0.5 oz {choice(sweet)}\n",
"\n",
"{choice(omph)}\n",
"\n",
"1 {choice(decor)}\n",
"\n",
"\n",
"*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*\n",
"\n",
"EXTRA: {choice(snack)}\n",
"\n",
"*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*\n",
"\n",
"\n",
" ___, \n",
" '._.'\\ \n",
" _____/'-.\\ \n",
" | / | \n",
" |~~~/~~| \n",
" \\ () / \n",
" '.__.' \n",
" || \n",
" _||_ \n",
" `----` \n",
"\n",
"\n",
"THANKS FOR COMING TO OUR LAUNCH!\n",
"\n",
"{choice(XPUB1)}\n",
"\n",
"\n",
"*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*\n",
"*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*\n",
"\n",
"</pre>\n",
" \"\"\"\n",
" \n",
"\n",
"\n",
" return recipe"
]
},
{
"cell_type": "markdown",
"id": "d50511cb-a810-4fe1-a681-e5cb1392c0d0",
"metadata": {},
"source": [
"![cocktail_generator](https://hub.xpub.nl/soupboat/~chae/vernacular-cocktail.jpg)"
]
},
{
"cell_type": "markdown",
"id": "1094ed8e-1387-481e-b612-9a8cc81d5c18",
"metadata": {},
"source": [
"This function generates amazing cocktail recipes for you to try! The cocktail menu includes the categories alcohol, base, sour, sweet and omph. There is also a category for a decorative gadget and one for an extra snack. Each category has a default list of ingredients and the function randomly chooses one ingredient per category. You prefer Rum and Tequila over Vodka? Or maybe you want a non-alcoholic cocktail? Don´t worry! For the categories alcohol and decor you can insert your own ingredients to pick from. Enjoy!\n",
"\n",
" Menu:\n",
"\n",
" default ingredients:\n",
" base = ORANGE JUICE, PINEAPPLE JUICE, APPLE JUICE, MANGO JUICE, FRIZZY WATER, GRAPEFRUIT JUICE, TONIC WATER\n",
" sour = LEMON JUICE, LIME JUICE, PASSION FRUIT\n",
" sweet = AGAVE SIRUP, SUGAR SIRUP, MAPLE SIRUP\n",
" omph = 1 SLICE GINGER, 2 LEAVES MINT, 1 SLICE CUCUMBER, 1 STICK CINNAMON, SALT RIM \n",
" snack = CHIPS, SALTED CORN, PRETZELS, SALTED NUTS\n",
"\n",
" users ingredients:\n",
" alcohol =\n",
" decor =\n"
]
},
{
"cell_type": "markdown",
"id": "2c3e992a-317b-4433-8c3b-9efcedea575d",
"metadata": {
"tags": []
},
"source": [
"## Examples\n",
"\n",
"In this exmaple the function chooses between three alcoholic ingredients and two decoratin gadgets"
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "e3d7e97b-0089-4c29-a2c1-9ef823ff2e37",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
"<pre>\n",
" \n",
"*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*\n",
"*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*\n",
"* *\n",
"* CATWALKING WITH ALCOHOL *\n",
"* *\n",
"* 2021-12-14 14:08:04 *\n",
"* *\n",
"*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*\n",
"*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*\n",
"\n",
"\n",
"\n",
"2 oz VODKA\n",
"\n",
"150 ml GRAPEFRUIT JUICE\n",
"\n",
"1 oz LEMON JUICE\n",
"\n",
"0.5 oz SUGAR SIRUP\n",
"\n",
"1 SLICE CUCUMBER\n",
"\n",
"1 UMBRELLA\n",
"\n",
"\n",
"*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*\n",
"\n",
"EXTRA: SALTED CORN\n",
"\n",
"*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*\n",
"\n",
"\n",
" ___, \n",
" '._.'\\ \n",
" _____/'-.\\ \n",
" | / | \n",
" |~~~/~~| \n",
" \\ () / \n",
" '.__.' \n",
" || \n",
" _||_ \n",
" `----` \n",
"\n",
"\n",
"THANKS FOR COMING TO OUR LAUNCH!\n",
"\n",
" (^(^(^(^(^.(^.^).^)^)^)^)^)^)\n",
"\n",
"\n",
"*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*\n",
"*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*\n",
"\n",
"</pre>\n",
" \n"
]
}
],
"source": [
"print(cocktail_generator(['VODKA', 'TEQUILA', 'RUM'], ['UMBRELLA', 'PALMTREE', 'FLAMINGO']))"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "6e79116b-12d7-4b6e-a764-f7a8fc14c1ce",
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.7.3"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -0,0 +1,172 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "66f72396-0cf1-41cf-a486-2930d2ad1652",
"metadata": {},
"source": [
"# individual_map\n",
"give a string with the name of the image-file that was annotated with the Annotation Compass; Select one or more specific targets and return a list of all labels that include these targetsselect one or more specific users and return a list of all labels from these users."
]
},
{
"cell_type": "code",
"execution_count": 6,
"id": "de97fe2f-daae-4293-a0b3-ea42c759535a",
"metadata": {},
"outputs": [],
"source": [
"from urllib.request import urlopen\n",
"import json"
]
},
{
"cell_type": "code",
"execution_count": 7,
"id": "9bd8860a-9967-49d5-a74a-b5de669dfe6d",
"metadata": {},
"outputs": [],
"source": [
"def individual_map(image: str, users: list ) -> list:\n",
" \n",
" \"\"\"give a string with the name of the image-file that was annotated with the Annotation Compass; Select one or more specific targets and return a list of all labels that include these targetsselect one or more specific users and return a list of all labels from these users.\"\"\" \n",
"\n",
" url = f\"https://hub.xpub.nl/soupboat/generic-labels/get-labels/?image={image}\"\n",
" response = urlopen(url)\n",
" data_json = json.loads(response.read()) \n",
" \n",
" filtered_map = []\n",
" for label in data_json['labels']:\n",
" for user in users:\n",
" if label['userID'] == user:\n",
" filtered_map.append(label)\n",
" return filtered_map"
]
},
{
"cell_type": "markdown",
"id": "1094ed8e-1387-481e-b612-9a8cc81d5c18",
"metadata": {},
"source": [
"This function was built for a project where individuals are invited to add their annotations on a map using the Annotation Compass. Each annotation-label is stored in a json-file and includes the annotation-text itself, but also the name of the image-file as well as the position, size, index, timestamp and userID of the annotation.\n",
"\n",
" Example for a label:\n",
"\n",
" {'image': 'map.jpg',\n",
" 'position': {'x': 12, 'y': 97},\n",
" 'size': {'width': 43, 'height': 18},\n",
" 'text': 'This is a text! Is this a text?',\n",
" 'timestamp': 'Wed, 01 Dec 2021 14:04:00 GMT',\n",
" 'userID': 5766039063}\n",
"\n",
"\n",
"If interested in all annotations of one or more specific users, individual_map() can help. The function needs a string with the name of the of the image-file that was annotated with the annotation compass tool as well as one or more specific userIDs to target. The output is a list of all labels from the users of interest.\n",
"\n",
"How to get a json-file with annotation-labels?\n",
"\n",
" https://hub.xpub.nl/soupboat/generic-labels/\n",
"The Annotation Compass allows people to uplaod an image and ask others to annotate it. A json-file of the annotations is provided."
]
},
{
"cell_type": "markdown",
"id": "2c3e992a-317b-4433-8c3b-9efcedea575d",
"metadata": {
"tags": []
},
"source": [
"## Examples\n",
"\n",
"In this example, the function returns all annotation-labels of two specific users."
]
},
{
"cell_type": "code",
"execution_count": 5,
"id": "e3d7e97b-0089-4c29-a2c1-9ef823ff2e37",
"metadata": {
"scrolled": true,
"tags": []
},
"outputs": [
{
"data": {
"text/plain": [
"[{'image': 'rejection_map.jpg',\n",
" 'position': {'x': 63.9896, 'y': 34.2958},\n",
" 'size': {'height': 3.23944, 'width': 2.90155},\n",
" 'text': 'here, someone called my behaviour \"strange\"',\n",
" 'timestamp': 'Wed, 15 Dec 2021 11:36:04 GMT',\n",
" 'userID': '8112114057'},\n",
" {'image': 'rejection_map.jpg',\n",
" 'position': {'x': 39.3264, 'y': 46.831},\n",
" 'size': {'height': 6.19718, 'width': 9.2228},\n",
" 'text': 'here, someone screamed at me \"move fucking chinee\"',\n",
" 'timestamp': 'Wed, 15 Dec 2021 11:36:55 GMT',\n",
" 'userID': '8112114057'},\n",
" {'image': 'rejection_map.jpg',\n",
" 'position': {'x': 36.5285, 'y': 54.2958},\n",
" 'size': {'height': 1.69014, 'width': 3.31606},\n",
" 'text': 'here, someone screamed at me, right into my face, something I could not understand',\n",
" 'timestamp': 'Wed, 15 Dec 2021 11:37:37 GMT',\n",
" 'userID': '8112114057'},\n",
" {'image': 'rejection_map.jpg',\n",
" 'position': {'x': 42.5389, 'y': 45.7042},\n",
" 'size': {'height': 13.662, 'width': 23.9378},\n",
" 'text': 'here, someone told me to move away',\n",
" 'timestamp': 'Wed, 15 Dec 2021 11:38:04 GMT',\n",
" 'userID': '8112114057'},\n",
" {'image': 'rejection_map.jpg',\n",
" 'position': {'x': 39.1192, 'y': 36.2676},\n",
" 'size': {'height': 11.6901, 'width': 20.4145},\n",
" 'text': 'here, someone told to stop looking at them',\n",
" 'timestamp': 'Wed, 15 Dec 2021 11:38:45 GMT',\n",
" 'userID': '8112114057'},\n",
" {'image': 'rejection_map.jpg',\n",
" 'position': {'x': 46.3713, 'y': 58.4289},\n",
" 'size': {'height': 6.99541, 'width': 15.1055},\n",
" 'text': 'somewhere between this line i lost my ID card. and I really wasnt aware that this could linger a lot my registration to the municipality. This kinda triggered but feeling for me, as I felt a lot disorented here, having no formal document verifying who I am and feeling guilty tat I am still not registered.',\n",
" 'timestamp': 'Wed, 15 Dec 2021 11:40:54 GMT',\n",
" 'userID': '6004575665'}]"
]
},
"execution_count": 5,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"individual_map('rejection_map.jpg', ['8112114057', '6004575665'])"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "fcbbb728-217d-47a3-b672-d03d120fa408",
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.7.3"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

@ -0,0 +1,258 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "b774afd4-4a80-48c5-a09a-7623429c7f0a",
"metadata": {},
"source": [
"# Mashup()\n"
]
},
{
"cell_type": "markdown",
"id": "626002a1-958c-4a31-bf75-50cd5dfab433",
"metadata": {},
"source": [
"**Mashup()** is a function that compare **two similar texts** and produce **a third text** that randomly choose between the original texts. \n",
"The outcome is a text with the differences chosen through random choice.\n",
"\n",
"A function that: \n",
"1. takes into account **2 similar texts** (example: 2 different translations of a poems)\n",
"2. finds the **fixed_words** and uses them as the fixed text for the new piece of text\n",
"3. puts the results together into **a new piece**, randomly choosing the different options \n",
"4. html output that **highlights the different random choices** of the translations\n"
]
},
{
"cell_type": "markdown",
"id": "b3e00ed0-59e2-4e1c-aa23-fd412fc8524b",
"metadata": {},
"source": [
"**how to use**: to use this function it is necessary to have two texts with the same number of lines (as it goes throught the two texts and compares them line by line). It can be use also with list of strings."
]
},
{
"cell_type": "markdown",
"id": "04e48da5-eeae-440d-8f8a-5952d6706d81",
"metadata": {},
"source": [
"**input:** 2 texts that are similar but not the exact copy of each other -- "
]
},
{
"cell_type": "markdown",
"id": "e8b2ab5c-3b41-4dce-8490-8d7052cdb858",
"metadata": {},
"source": [
"**output:** a new text that showcase the differences // a new text made out of random choice // *still not clear yet* // extracted pdf/txt file?"
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "e6d5f744-d079-4a51-a5c2-6845a7a7ac56",
"metadata": {},
"outputs": [],
"source": [
"# define texts\n",
"\n",
"text1= '''\n",
"The glasses were empty\n",
"The bottle was shattered\n",
"The bed was wide open\n",
"The door was tight shuttered\n",
"Each shard was a star\n",
"Of bliss and of beauty\n",
"That flashed on the floor\n",
"All dusty and dirty\n",
"And I was dead drunk\n",
"Lit up wildly ablaze\n",
"You were drunk and alive\n",
"In a naked embrace!\n",
"'''\n",
"text2= '''\n",
"So the glasses were empty\n",
"and the bottle broken\n",
"And the bed was wide open\n",
"and the door closed\n",
"And all of the glass stars\n",
"of happiness and beauty \n",
"were sparkling in the dust\n",
"of the poorly dusted room.\n",
"And I was dead drunk\n",
"And I was a bonfire\n",
"And you were alive, drunk,\n",
"all naked in my arms.\n",
"'''\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "a8f10c45-0ce7-4ef8-9a5f-6a92f69902a2",
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "code",
"execution_count": 3,
"id": "3c62af9c-b078-4117-b84b-e191048c6a93",
"metadata": {},
"outputs": [],
"source": [
"import difflib\n",
"from random import choice\n",
"\n",
"def mashup(text1,text2): #take into account 2 texts\n",
" \n",
" text1 = text1.splitlines() #split texts in lines\n",
" text2 = text2.splitlines()\n",
" \n",
" fixed_words= [] #define empty list for fixed_words (words that are the same in both texts) // a list of lists of words\n",
" for line_A, line_B in zip(text1, text2): #start the first loop reading line by line from both texts at the same time (=zip)\n",
" words_A = line_A.split() #split lines in lists of words\n",
" words_B = line_B.split()\n",
" \n",
" d = difflib.Differ() #Differ compare sequences of lines of text, and produce human-readable differences ('+' in text1), ('-' in text2), ('' fixed_Words)\n",
" diff = d.compare(words_A, words_B) #compare the difference between the two lists of words\n",
" \n",
" \n",
" linelist = [] #define empty list \n",
" for result in diff: #second loop that goes through all the lines and then the words of both texts simultaneously\n",
" code, word = result.split(' ', 1) #split result of diff in code [('+'), ('-') or ('')] and the resulting word (is it the same or is it just in one of the two texts?)\n",
" word = word.strip() #to be sure it doesn't have any weird things /n at the ends of the lines\n",
" if code == '' : #if the code is ' ' (nothing) it means that the word can be found in both texts\n",
" linelist.append(word) #if this happens, put the corresponding words in the linelist\n",
" fixed_words.append(linelist) #afterwards, add linelist to fixed_words (linelist is inside the loop so all the words in every line are put in there, but fixed_words is outside so that just the words are added just once)\n",
" \n",
" length = len(text1) #define lenght of text1\n",
" for linenumber in range(length): #for the number of the lines in the lenght of the text\n",
" cut_left1 = 0 #the beginning of both texts is position n°0 (on the left side of the lines)\n",
" cut_left2 = 0\n",
" words_1 = text1[linenumber].split() #words_1 is split in words keeping the position in the lines\n",
" words_2 = text2[linenumber].split() \n",
" if len(fixed_words[linenumber]) > 0: #if the index on the fixed words in the line is more than 0 (it's not the first one)\n",
" for fixed_word in fixed_words[linenumber]: #for all the fixed_words that are in the fixed_words list always following the linenumbers\n",
" cut_right1 = words_1.index(fixed_word) #finding the first fixed_word from the left (beginning / position 0) to the right(end of sentence / last word in the line)\n",
" cut_right2 = words_2.index(fixed_word) #in both texts\n",
"\n",
" slice_1 = words_1[cut_left1 : cut_right1] #create slice_1 \n",
" slice_2 = words_2[cut_left2 : cut_right2]\n",
" print(choice([slice_1, slice_2]))\n",
" \n",
" cut_left1 = cut_right1 #now invert, when it's gone through all the words till finding the last fixed word\n",
" cut_left2 = cut_right2\n",
"\n",
" slice_1 = words_1[cut_left1 :] #from the last fixed_word found to the right\n",
" slice_2 = words_2[cut_left2 :]\n",
" print(choice([slice_1, slice_2])) #choose\n",
" else:\n",
" slice_1 = words_1[cut_left1 :] #here is doing it outside of the loop ( it gets the last word of the line if it's not a\n",
" slice_2 = words_2[cut_left2 :]\n",
" print(choice([slice_1, slice_2])) #choose\n",
" print('--------') \n",
" "
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "78c7643c-e797-451e-872d-e6dd81f0052c",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[]\n",
"--------\n",
"['So', 'the']\n",
"['glasses']\n",
"['were']\n",
"['empty']\n",
"--------\n",
"['and', 'the']\n",
"['bottle', 'was', 'shattered']\n",
"--------\n",
"['The']\n",
"['bed']\n",
"['was']\n",
"['wide']\n",
"['open']\n",
"--------\n",
"['and', 'the']\n",
"['door', 'was', 'tight', 'shuttered']\n",
"--------\n",
"['And', 'all', 'of', 'the', 'glass', 'stars']\n",
"--------\n",
"['of', 'happiness']\n",
"['and']\n",
"['beauty']\n",
"--------\n",
"['were', 'sparkling', 'in']\n",
"['the', 'dust']\n",
"--------\n",
"['All', 'dusty', 'and', 'dirty']\n",
"--------\n",
"[]\n",
"['And']\n",
"['I']\n",
"['was']\n",
"['dead']\n",
"['drunk']\n",
"--------\n",
"['And', 'I', 'was', 'a', 'bonfire']\n",
"--------\n",
"['And', 'you']\n",
"['were', 'alive,', 'drunk,']\n",
"--------\n",
"['In', 'a']\n",
"['naked', 'in', 'my', 'arms.']\n",
"--------\n"
]
}
],
"source": [
"mashup(text1,text2)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "07f2e386-3380-4dc0-b680-d5b1288e7794",
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "code",
"execution_count": null,
"id": "0b127fbb-6e70-4614-a727-2ec476e43236",
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.7.3"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

@ -0,0 +1,124 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "66f72396-0cf1-41cf-a486-2930d2ad1652",
"metadata": {},
"source": [
"# Repeat\n",
"Repeat a string for a specified number of times"
]
},
{
"cell_type": "code",
"execution_count": 1,
"id": "9bd8860a-9967-49d5-a74a-b5de669dfe6d",
"metadata": {},
"outputs": [],
"source": [
"def repeat(text: str, times: int = 2) -> str:\n",
" \"\"\"Repeat a string for a specified number of times\"\"\"\n",
" return text * times"
]
},
{
"cell_type": "markdown",
"id": "d50511cb-a810-4fe1-a681-e5cb1392c0d0",
"metadata": {},
"source": [
"![ara repeating itself](https://www.dienst.nl/sub/upload/images/1/30019_550.jpg)"
]
},
{
"cell_type": "markdown",
"id": "1094ed8e-1387-481e-b612-9a8cc81d5c18",
"metadata": {},
"source": [
"This function has many attractive qualities, but its ability to repeat human speech is one that makes it truly unique among other types of companion python functions and one that has ensured its popularity for generations. You are likely to find, though, that the function's talents for mimicry still pales in comparison to the fact that it is charming, engaging, and truly remarkable. Here is one of the most popular repeating function so that you can appreciate more about what it has to offer. It often says injuries to people and computers. "
]
},
{
"cell_type": "markdown",
"id": "2c3e992a-317b-4433-8c3b-9efcedea575d",
"metadata": {
"tags": []
},
"source": [
"## Examples\n",
"\n",
"The function takes a string as a parameter, and by default it repeats it twice. "
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "e3d7e97b-0089-4c29-a2c1-9ef823ff2e37",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"'hellohello'"
]
},
"execution_count": 2,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"repeat('hello')"
]
},
{
"cell_type": "markdown",
"id": "d3ce6961-68ff-4338-a71f-f8d77aa4f949",
"metadata": {},
"source": [
"Eventually with a second parameter you can specify how many times you want it to repeats. "
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "dd3f3a0b-3145-4914-8638-951bcdbd0774",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"'salutsalutsalutsalut'"
]
},
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"repeat('salut', 4)"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.7.3"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

@ -0,0 +1,133 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "4b1d288c-50c0-4da2-9ea9-bf5adb06b034",
"metadata": {},
"source": [
"# Respell\n",
"Respell receives as input a text as a string type, and substitute all the occurrences of a targeted word with a replacement as a string type chosen by the user."
]
},
{
"cell_type": "code",
"execution_count": 1,
"id": "691152f6-8e85-4fc2-8c98-848448ee959a",
"metadata": {},
"outputs": [],
"source": [
"from nltk.tokenize import word_tokenize"
]
},
{
"cell_type": "code",
"execution_count": 5,
"id": "2a46fe37-b62f-482a-a040-9f7a5d39fc0e",
"metadata": {},
"outputs": [],
"source": [
"# text, target, and replacement are string types\n",
"def respell(text, target, replacement):\n",
" target = target.lower()\n",
" txt = word_tokenize(text)\n",
" new = []\n",
" \n",
" for w in txt:\n",
" if w == target:\n",
" w = replacement\n",
" new = new + [w]\n",
" elif w == target[0:1].upper() + target[1:]:\n",
" w = replacement[0:1].upper() + replacement[1:] \n",
" new = new + [w]\n",
" elif w == target.upper():\n",
" w = replacement.upper()\n",
" new = new + [w]\n",
" else:\n",
" new = new + [w]\n",
" text = ' '.join(new)\n",
" final= text.replace(' .','.').replace(' ,',',').replace(' :',':').replace(' ;',';').replace('< ','<').replace(' >','>').replace(' / ','/').replace('& ','&')\n",
" return final"
]
},
{
"cell_type": "markdown",
"id": "63374a00-6091-484d-9ffd-ebf79d13cdc1",
"metadata": {},
"source": [
"This function in itself could be understood as a filter to process and alter texts. By targeting specific words and replacing them, either for another word, for specific characters or for blank spaces, the user of the tool can intervene inside a text. One could break down the meaning of a text or create new narrative meanings by exposing its structure, taking out or highlighting specific and meaningful words and detaching such text from its original context. \n",
"This tool offers a broad spectrum of possibilities in which it can be used, from a very political and subversive use, to a more playful and poetic one."
]
},
{
"cell_type": "markdown",
"id": "1fde0e28-f253-4992-96d1-0497da2959f6",
"metadata": {},
"source": [
"# Examples"
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "6c80de23-d4ad-45a7-8b2f-b07c4ff2e549",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"'potato is potato'"
]
},
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"respell(\"life is life\",\"life\",\"potato\")"
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "772ae978-5551-486e-add1-0a5a660a0bff",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"'🥙 is 🥙'"
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"respell(\"life is life\",\"life\",\"🥙\")"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.7.3"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

@ -0,0 +1,189 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "f0e7e936-865b-4389-b180-e07eaf8f4cfc",
"metadata": {},
"source": [
"# Reveal\n",
"Reveal takes a text as string input and deletes all its characters except the input list of words."
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "f920735b-ed2a-4dd6-8c28-95b4dafad70a",
"metadata": {},
"outputs": [],
"source": [
"from nltk.tokenize import word_tokenize"
]
},
{
"cell_type": "code",
"execution_count": 11,
"id": "2c72ec84-7f91-4d1e-b9f9-6629ab4fae94",
"metadata": {},
"outputs": [],
"source": [
"def reveal(text,group):\n",
" txt = word_tokenize(text)\n",
" \n",
" txt_linebr = []\n",
" for token in txt:\n",
" if token == '<':\n",
" continue\n",
" elif token == 'br/':\n",
" token='<br/>'\n",
" txt_linebr.append(token)\n",
" elif token == '>':\n",
" continue\n",
" else:\n",
" txt_linebr.append(token) \n",
" new = []\n",
" for w in txt_linebr:\n",
" if w=='<br/>':\n",
" new = new + [w]\n",
" elif w not in group:\n",
" w = len(w) * ' '\n",
" new = new + [w]\n",
" elif w in group :\n",
" new = new + [w]\n",
" text = ' '.join(new)\n",
" final= text.replace(' .','.').replace(' ,',',').replace(' :',':').replace(' ;',';').replace('< ','<').replace(' >','>').replace(' / ','/').replace('& ','&')\n",
" return final"
]
},
{
"cell_type": "markdown",
"id": "cff36759-e377-48d5-93f8-d78f6f9a3050",
"metadata": {},
"source": [
"This function in itself could be understood as a filter to process and alter texts. By chosing to keeping specific words of a text and deleting all the others, the user of the tool can intervene inside a text. One could break down the meaning of a text or create new narrative meanings by exposing its structure, taking out or highlighting specific and meaningful words and detaching such text from its original context. \n",
"This tool offers a broad spectrum of possibilities in which it can be used, from a very political and subversive use, to a more playful and poetic one."
]
},
{
"cell_type": "code",
"execution_count": 12,
"id": "a6bce73e-8084-4bd0-b7b8-95cd39069030",
"metadata": {},
"outputs": [],
"source": [
"text = f\"\"\"\n",
"Live\n",
"Live is life\n",
"Live\n",
"Live\n",
"\n",
"When we all give the power\n",
"We all give the best\n",
"Every minute of an hour\n",
"Don't think about a rest\n",
"Then you all get the power\n",
"You all get the best\n",
"When everyone gives everything\n",
"And every song everybody sings\n",
"\n",
"Then it's live\n",
"Live is life\n",
"Live is life\n",
"Live\n",
"\n",
"Live is life, when we all feel the power\n",
"Live is life, come on stand up and dance\n",
"Live is life, when the feeling of the people\n",
"Live is life, is the feeling of the band\n",
"\n",
"When we all give the power\n",
"We all give the best\n",
"Every minute of an hour\n",
"Don't think about a rest\n",
"Then you all get the power\n",
"You all get the best\n",
"When everyone gives everything\n",
"And every song everybody sings\n",
"\n",
"Then it's live\n",
"Live is life\n",
"Live\n",
"Live is life\n",
"Live\n",
"\n",
"Live\n",
"Live is life\n",
"Live\n",
"Live is life\n",
"\n",
"And you call when it's over\n",
"You call it should last\n",
"Every minute of the future\n",
"Is a memory of the past\n",
"'Cause we all gave the power\n",
"We all gave the best\n",
"And everyone gave everything\n",
"And every song everybody sang\n",
"Live is life\n",
"\"\"\""
]
},
{
"cell_type": "markdown",
"id": "8d6dfda4-122b-42cc-8d29-5d141d726c47",
"metadata": {},
"source": [
"# Examples"
]
},
{
"cell_type": "code",
"execution_count": 16,
"id": "89d83728-6e1c-4270-b0bc-6cfb59f4f389",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"'Live Live is life Live Live live Live is life Live is life Live Live is life Live is life Live is life Live is life is live Live is life Live Live is life Live Live Live is life Live Live is life Live is life'"
]
},
"execution_count": 16,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"reveal(text,[\"Live\",\"is\",\"life\",\"live\"])"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "8ec2e799-7e05-42c3-ae4c-84a299b66a2d",
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.7.3"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

@ -0,0 +1,71 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "5688f740-c1a2-4271-880d-2be65c748d94",
"metadata": {},
"source": [
"# REVERSE!"
]
},
{
"cell_type": "code",
"execution_count": 7,
"id": "df889e4f-3ccc-4411-b4c7-53c8098661c9",
"metadata": {},
"outputs": [],
"source": [
"from textblob import TextBlob\n",
"\n",
"def reverse (inputz: str) -> str:\n",
" \"\"\"Reverse the input sentence by sentence\"\"\"\n",
" blob = TextBlob(inputz)\n",
" return \"\\n\".join(str(reversed(blob.sentences)))\n",
" \n",
" # return \" \".join(reversed(inputz.split()))"
]
},
{
"cell_type": "code",
"execution_count": 8,
"id": "c481d133-0fc4-4046-8f50-0e5cea1c0e11",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"'<\\nl\\ni\\ns\\nt\\n_\\nr\\ne\\nv\\ne\\nr\\ns\\ne\\ni\\nt\\ne\\nr\\na\\nt\\no\\nr\\n \\no\\nb\\nj\\ne\\nc\\nt\\n \\na\\nt\\n \\n0\\nx\\na\\nc\\n4\\na\\nf\\n3\\n9\\n0\\n>'"
]
},
"execution_count": 8,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"reverse(\"hello world\")"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.7.3"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

@ -0,0 +1,158 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "66f72396-0cf1-41cf-a486-2930d2ad1652",
"metadata": {},
"source": [
"# Shout\n",
"This function take a text and shouts it LOUD. It repeats the vowels for a specified number of times."
]
},
{
"cell_type": "code",
"execution_count": 1,
"id": "9bd8860a-9967-49d5-a74a-b5de669dfe6d",
"metadata": {},
"outputs": [],
"source": [
"def shout(text: str, volume: int = 5) -> str:\n",
" \"\"\"Repeat the vowels in a string for a specified number of times\"\"\"\n",
" shouted_text = ''\n",
" for c in text:\n",
" character = c\n",
" if c.lower() in ['a','e','i','o','u','y']:\n",
" character = character * volume\n",
" shouted_text = shouted_text + character\n",
" return shouted_text"
]
},
{
"cell_type": "markdown",
"id": "d50511cb-a810-4fe1-a681-e5cb1392c0d0",
"metadata": {},
"source": [
"![Snake on a sbake](https://hub.xpub.nl/soupboat/~kamo/assets/goose.jpg)"
]
},
{
"cell_type": "markdown",
"id": "2c3e992a-317b-4433-8c3b-9efcedea575d",
"metadata": {
"tags": []
},
"source": [
"## Examples"
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "e3d7e97b-0089-4c29-a2c1-9ef823ff2e37",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"'heeeeelp'"
]
},
"execution_count": 2,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"shout('help')"
]
},
{
"cell_type": "markdown",
"id": "d3ce6961-68ff-4338-a71f-f8d77aa4f949",
"metadata": {},
"source": [
"The first parameter is the text you want to shout. It can be anything, like a terrorized cry or a drunk song at 4am outside the pub. \n",
"If you want to scream louder you can specify a second parameter for the volume. This is a number and by default is 5. It has to be positive but there are no limits to it."
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "dd3f3a0b-3145-4914-8638-951bcdbd0774",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"'HEEEEEEEEEELP'"
]
},
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"shout('HELP', 10)"
]
},
{
"cell_type": "markdown",
"id": "d7e70281-0562-4fb0-8c07-b2ea7143b393",
"metadata": {},
"source": [
"You see, this is very a dangerous situation."
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "9f685cab-278e-4b44-afd9-6e6012981442",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"'MG HLP'"
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"shout('OMG HELP', 0)"
]
},
{
"cell_type": "markdown",
"id": "0f668b5e-43bf-4321-b05b-5930d6eac152",
"metadata": {},
"source": [
"A muted scream"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.7.3"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

@ -0,0 +1,141 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "af317257-97a8-46db-ac92-e06bdef22ed3",
"metadata": {},
"source": [
"# Stitch\n",
"Stitch receives as input a text as a string type, and replaces all the occurrences of a target word, with a character or a word that is repeated as many times as the length of the target. "
]
},
{
"cell_type": "code",
"execution_count": 1,
"id": "8c280e90-22dc-4e59-90fe-3bdf97da06b2",
"metadata": {},
"outputs": [],
"source": [
"from nltk.tokenize import word_tokenize"
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "f26e528c-1d61-4796-a42a-a3d5d0edc574",
"metadata": {},
"outputs": [],
"source": [
"# text, target, and replacement are string types\n",
"def stich(text, target, replacement):\n",
" target = target.lower()\n",
" txt = word_tokenize(text)\n",
" new = []\n",
" \n",
" for w in txt:\n",
" if w == target:\n",
" w = len(w)*replacement\n",
" new = new + [w]\n",
" elif w == target[0].upper() + target[1:]:\n",
" w = len(w)*replacement\n",
" new = new + [w]\n",
" elif w== target.upper():\n",
" w = len(w)*replacement \n",
" new = new + [w]\n",
" else:\n",
" new = new + [w]\n",
" text = ' '.join(new)\n",
" final= text.replace(' .','.').replace(' ,',',').replace(' :',':').replace(' ;',';').replace('< ','<').replace(' >','>').replace(' / ','/').replace('& ','&')\n",
" return final"
]
},
{
"cell_type": "markdown",
"id": "7e5443c5-e6d3-4dca-b196-7f8c0204814f",
"metadata": {},
"source": [
"This function in itself could be understood as a filter to process and alter texts. By targeting specific words and stitching them, with a character or a word that is repeated as many times as the length of the target , the user of the tool can intervene inside a text. One could break down the meaning of a text or create new narrative meanings by exposing its structure, taking out or highlighting specific and meaningful words and detaching such text from its original context. \n",
"This tool offers a broad spectrum of possibilities in which it can be used, from a very political and subversive use, to a more playful and poetic one."
]
},
{
"cell_type": "markdown",
"id": "e054e805-9abb-4751-af1f-0039975520d6",
"metadata": {},
"source": [
"# Examples"
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "32c73229-7914-4b16-8054-a74a6869ede8",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"' is '"
]
},
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"stich(\"life is life\",\"life\",\" \")"
]
},
{
"cell_type": "code",
"execution_count": 5,
"id": "9d273092-5cda-4d75-ad1c-9473bf65e69a",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"'**** is ****'"
]
},
"execution_count": 5,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"stich(\"life is life\",\"life\",\"*\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "b0805582-fa73-43b5-b6f9-01cfedd4003d",
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.7.3"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

@ -0,0 +1,190 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "66f72396-0cf1-41cf-a486-2930d2ad1652",
"metadata": {},
"source": [
"# target_map\n",
"Give a string with the name of the image-file that was annotated with the Annotation Compass; Select one or more specific targets and return a list of all labels that include these targets"
]
},
{
"cell_type": "code",
"execution_count": 5,
"id": "dc7040fd-ec5d-49e6-98e5-1feae91fc74b",
"metadata": {},
"outputs": [],
"source": [
"from urllib.request import urlopen\n",
"import json"
]
},
{
"cell_type": "code",
"execution_count": 6,
"id": "9bd8860a-9967-49d5-a74a-b5de669dfe6d",
"metadata": {},
"outputs": [],
"source": [
"def target_map(image: str, targets: list ) -> list:\n",
" \n",
" \"\"\"Give a string with the name of the image-file that was annotated with the Annotation Compass; Select one or more specific targets and return a list of all labels that include these targets.\"\"\" \n",
"\n",
" url = f\"https://hub.xpub.nl/soupboat/generic-labels/get-labels/?image={image}\"\n",
" response = urlopen(url)\n",
" data_json = json.loads(response.read()) \n",
" \n",
" filter_map = []\n",
" for label in data_json['labels']:\n",
" for target in targets:\n",
" if target in label['text']:\n",
" filter_map.append(label)\n",
" return filter_map"
]
},
{
"cell_type": "markdown",
"id": "1094ed8e-1387-481e-b612-9a8cc81d5c18",
"metadata": {},
"source": [
"This function was built for a project where individuals are invited to add their annotations on a map using the Annotation Compass. Each annotation-label is stored in a json-file and includes the annotation-text itself, but also the name of the image-file as well as the position, size, index, timestamp and userID of the annotation.\n",
"\n",
" Example for a label:\n",
"\n",
" {'image': 'map.jpg',\n",
" 'position': {'x': 12, 'y': 97},\n",
" 'size': {'width': 43, 'height': 18},\n",
" 'text': 'This is a text! Is this a text?',\n",
" 'timestamp': 'Wed, 01 Dec 2021 14:04:00 GMT',\n",
" 'userID': 5766039063}\n",
"\n",
"\n",
"If interested in all annotations that include one or more specific targets, target_map() can help. The function needs a string with the name of the of the image-file that was annotated with the annotation compass tool as well as one or more specific targets. The output is a list of all labels that include these targets.\n",
"\n",
"How to get a json-file with annotation-labels?\n",
"\n",
" https://hub.xpub.nl/soupboat/generic-labels/\n",
"The Annotation Compass allows people to uplaod an image and ask others to annotate it. A json-file of the annotations is provided."
]
},
{
"cell_type": "markdown",
"id": "2c3e992a-317b-4433-8c3b-9efcedea575d",
"metadata": {
"tags": []
},
"source": [
"## Examples\n",
"\n",
"In this example, the function returns all annotation-labels that include two specific targets."
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "e3d7e97b-0089-4c29-a2c1-9ef823ff2e37",
"metadata": {
"scrolled": true,
"tags": []
},
"outputs": [
{
"data": {
"text/plain": [
"[{'image': 'rejection_map.jpg',\n",
" 'position': {'x': 48.135, 'y': 20.4458},\n",
" 'size': {'height': 4.62487, 'width': 2.94785},\n",
" 'text': 'house viewing 2: meeting with some girls in the evening to see a room; felt super exposed and awkward. Later on, they rejected me.',\n",
" 'timestamp': 'Wed, 15 Dec 2021 11:36:48 GMT',\n",
" 'userID': '1933684842'},\n",
" {'image': 'rejection_map.jpg',\n",
" 'position': {'x': 76.25, 'y': 69.5246},\n",
" 'size': {'height': 12.3939, 'width': 15.625},\n",
" 'text': \"I once went to view a house here but it didn't have the floor. crap.\",\n",
" 'timestamp': 'Wed, 15 Dec 2021 11:36:51 GMT',\n",
" 'userID': '6286616941'},\n",
" {'image': 'rejection_map.jpg',\n",
" 'position': {'x': 55.0889, 'y': 15.6154},\n",
" 'size': {'height': 4.21377, 'width': 3.55253},\n",
" 'text': \"the most unpleasant house viewing: my viewing overlapped with the previous person and I didn't got the chance to connect with the ones living them. How could then they know who I am and even consider me for the room?\",\n",
" 'timestamp': 'Wed, 15 Dec 2021 11:37:46 GMT',\n",
" 'userID': '1933684842'},\n",
" {'image': 'rejection_map.jpg',\n",
" 'position': {'x': 45.2507, 'y': 29.3907},\n",
" 'size': {'height': 6.09319, 'width': 6.59631},\n",
" 'text': \"I had the most awkward house viewing here. The people barely talked to me and really let me know that they didn't like me. Obviously, I did not get the room.\",\n",
" 'timestamp': 'Wed, 15 Dec 2021 11:38:09 GMT',\n",
" 'userID': '4287159985'},\n",
" {'image': 'rejection_map.jpg',\n",
" 'position': {'x': 42.0882, 'y': 18.8014},\n",
" 'size': {'height': 3.08325, 'width': 1.66289},\n",
" 'text': 'my first house viewing: I was really hopeful about this one, actually, because I felt a connection with the girl. I waited 2 weeks to be rejected from this one.',\n",
" 'timestamp': 'Wed, 15 Dec 2021 11:38:41 GMT',\n",
" 'userID': '1933684842'},\n",
" {'image': 'rejection_map.jpg',\n",
" 'position': {'x': 43.7995, 'y': 27.0609},\n",
" 'size': {'height': 9.319, 'width': 8.04749},\n",
" 'text': \"I had the most awkward house viewing here. The people barely talked to me and really let me know that they didn't like me. Not surprisingly, I did not get the room.\",\n",
" 'timestamp': 'Wed, 15 Dec 2021 11:39:12 GMT',\n",
" 'userID': '1165348288'},\n",
" {'image': 'rejection_map.jpg',\n",
" 'position': {'x': 55.621, 'y': 33.6313},\n",
" 'size': {'height': 15.6597, 'width': 7.43427},\n",
" 'text': 'we were going to a friends house when we got trapped into the riot against covid restrictions and a car got on fire and it was super bad to see all the anger all these people had i felt small and sad and i just wanted to run as faster as i could',\n",
" 'timestamp': 'Wed, 15 Dec 2021 11:43:45 GMT',\n",
" 'userID': '4164927552'},\n",
" {'image': 'rejection_map.jpg',\n",
" 'position': {'x': 55.621, 'y': 33.6313},\n",
" 'size': {'height': 15.6597, 'width': 7.43427},\n",
" 'text': 'we were going to a friends house when we got trapped into the riot against covid restrictions and a car got on fire and it was super bad to see all the anger all these people had i felt small and sad and i just wanted to run as faster as i could',\n",
" 'timestamp': 'Wed, 15 Dec 2021 11:43:45 GMT',\n",
" 'userID': '4164927552'},\n",
" {'image': 'rejection_map.jpg',\n",
" 'position': {'x': 33.8622, 'y': 34.9877},\n",
" 'size': {'height': 12.8237, 'width': 13.7806},\n",
" 'text': 'house rejection\\n',\n",
" 'timestamp': 'Wed, 15 Dec 2021 11:49:37 GMT',\n",
" 'userID': '4164927552'}]"
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"target_map('rejection_map.jpg', ['house', 'sad'])"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "0c91b182-7541-4a79-a0f2-b2e688dd4371",
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.7.3"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -0,0 +1,27 @@
KYRIAKOS MITSOTAKIS, GREEK PRIME MINISTER
I understand that in the Netherlands you have a culture of asking direct questions to politicians, which I very much respect.
What I will not accept is that, in this office, you will insult me, or the Greek people, with accusations and expressions that are not supported by material facts when this country has been dealing with a migration crisis of unprecedented intensity, has been saving hundreds, if not thousands of people at sea.
We just rescued 250 people in danger of drowning south of Crete, we are doing this every single day rescuing people at sea, while, at the same time, we are intercepting boats that come from Turkey, as we have the right to do in accordance with European regulations and waiting for the Turkish Coast Guard to come and pick them up and return them to Turkey.
So, rather than putting the blame on Greece, you should put the blame on those who have been instrumentalizing migration systematically pushing people in to a desperate situation from a safe country, because I need to remind you that people who are in Turkey are not in danger, their life is not in danger and you should put the blame on others and not us.
We have a tough, but fair, policy on migration, we have processed and given the right to protection in Greece to 50,000 people, including tens of thousands of Afghans, in accordance…
Allow me. Have you visited the new camps on our islands? Have you been to Samos? … No listen to me, you have not been to Samos… No you have not been…
Please…Look, you will not come into this building and insult me.
Am I very clear on this?
I am answering now and you will not interrupt me, in the same way that I listened to you very carefully.
If you go to Samos, you will find an impeccable camp, with impeccable conditions, funded by EU money, with clean facilities, with playgrounds for…the children to play, no comparison to what we had in the past.
This is our policy, we will stand by it, and I will not accept anyone pointing the finger to this government and accusing it of inhumane behavior.
MARK RUTTE, DUTCH PRIME MINISTER
I am absolutely convinced that this prime minister and this government is applying the highest standards and the fact that they have immediately launched an investigation on the issue of the pushbacks is testimony of that.
I will now go back on the situation of 2015 and 2016 when we had many people dying on the Aegean Sea trying to get from Turkey into Greece and then to Germany, Sweeden, the Netherlands etc. And I am happy that Germany and we -were holding at that time the rotating presidency of the EU- were able to negotiate the EU and Turkey agreement.
By which indeed Turkey is a safe country for people to stay.
And Turkey at this moment is hosting over 3 million Syrian refugees in the South of Turkey in camps but also in the local communities.
What this country is trying to do is to defend the outer borders of the European Union.
It is a lot of tasks that countries have who are lying on the outside like Italy, Spain, Hungary, Slovenia, but also Poland and Greece, and there is an extremely difficult situation.
What I dont want again is for people to take boats that are not fully equipped to pass the Mediterranean or to pass the Aegean Sea, to die in those circumstances.
I want them to stay there, to be safe, and then we are willing as European Union to take a fair share of people from Africa, from Turkey refugees, in line with the plans devised in 2015 and 2016.
So this is my answer and I wish that your question has been answered.

@ -0,0 +1,20 @@
INGEBORB BEUGEL, JOURNALIST
I have one question for both of you.
Prime Minister Mitsotakis, when at last will you stop lying, lying about pushbacks, lying about what is happening with the refugees in Greece?
Please dont insult mine and neither the intelligence of all the journalists in the world.
There has been overwhelming evidence and you keep denying and lying.
This is like narcissistic abuse.
Why are you not honest?
Why dont you say Brussels left us alone?
We waited for six years. Nobody did anything.
We need to relocate.
They dont do it.
Now, I have my say and yes I do cruel, barbarian pushbacks.
Why did you stop knocking on Brussels door for relocation?
For you Mr. Rutte, what according to you are the sanctions that should be imposed on Greece and maybe on Holland for accepting this violation of human rights that Holland is co-responsible of also?
Many, many municipalities in Holland want to take many refugees from Greece, like many minor unaccompanied children.
They are ready to accept them, but this prime minister [Mark Rutte] opposes to that, so maybe you could find an understanding and the Dutch municipality who are so ready to unburden Greece can actually take in refugees from Greece, which his [Mark Rutte's] government opposes.
Thank you.

@ -0,0 +1,32 @@
---
title: "...and I wish that your question has been answered"
description: "An iterative process of refusal towards PM Kryakos Mitsotakis and PM Mark Rutte's answers"
url: "https://hub.xpub.nl/soupboat/~grgr/api/and_i_wish_that_your_question_has_been_answered/"
colophon: "Carmen, Grgr, Miriam, Mitsa"
---
This is an act of persistent resistance. We created a few functions to facilitate an iterative process of refusal towards PM Kryakos Mitsotakis and PM Mark Rutte's answers during a Press Conference and any of their possible versions. We invite you to play as much as you want with these functions and create your own answers as counter-reaction to Mark Rutte's final sentence: "So this is my answer and I wish that your question has been answered".
Every new answer, every new iteration, can be submitted to our Archive of Repetitive Answers. Although they will never be good enough, nor shall they be accepted as exhaustive, we consider the modified answers as a trigger for a never-ending dialogue.
Our tool is a filter to process and alter texts. By targeting specific words and replacing them, either for another word, for specific characters or for blank spaces, the reader or user of the tool can change the text in many ways. The tool includes three functions
The function “respell” receives as input a text (string type) and substitute all the occurrences of a target word with a replacement chosen by the user.
The function “stitch” is very similar to the previous one but replaces all the occurrences of a target word with a single character (it can also be a blank space) that is repeated as many times as the length of the target.
The third function “reveal” also works very similar but deletes all input text except the target word(s) and replaces the deleted text with blank spaces.
![text processing](https://hub.xpub.nl/soupboat/si16-app/static/img/mitsotakis_test.jpg)
### colophon
“...and I wish that your question has been answered.”
Mitsa (Dimitra Chaida), Erica Gargaglione, Carmen Gray, Miriam Schöb
December 2021

@ -0,0 +1,742 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "4baa72c7-b196-4658-b8bb-b8aaf73fd631",
"metadata": {},
"source": [
"https://hub.xpub.nl/soupboat/~grgr/api/"
]
},
{
"cell_type": "markdown",
"id": "c719aba0-0866-4ea1-a235-1675984f34ef",
"metadata": {
"tags": []
},
"source": [
"# ... and I whish that your question has been answered\n",
"\n",
"\n",
"this tool includes two functions that intervene on text by replacing specific targeted words with either other words or single characters.\n",
"The 2 main functions are based in a very simple mechanism, but the scope and results one could get by using them in different ways and with different intentions can be very compelling.\n",
"One could break down the meaning of a text, by exposing its structure, taking out specific and meaningful words and detaching such text from its original context.\n",
"If then, these words would be substituted for others, the text can be given a new meaning, reclaiming concepts or ideas.\n",
"The words could be also erased and replaced for blank spaces in order to create a new narrative meaning with what is left on the page, as well as to create abstract compositions. \n",
"This tool offers a broad spectrum of possibilities in which it can be used, from a very political and subversive use, to a more playful and artistic (or poetic) one."
]
},
{
"cell_type": "markdown",
"id": "0cd66035-60a5-4df6-884d-1012b5fe1132",
"metadata": {},
"source": [
"### import"
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "5ace74f9-7dde-4944-9b49-878020a38bae",
"metadata": {},
"outputs": [],
"source": [
"from flask import Flask, request, json, render_template, url_for, redirect\n",
"from jinja2 import Environment, PackageLoader, select_autoescape\n",
"from weasyprint import HTML, CSS\n",
"from nltk.tokenize import word_tokenize\n",
"\n",
"\n",
"import os \n",
"import json"
]
},
{
"cell_type": "markdown",
"id": "a85a6dfa-25b9-4cb0-808e-823f35bdf2cb",
"metadata": {},
"source": [
"for the receipt printer:"
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "9a22628a-123f-48a6-8b6a-2bea1918acec",
"metadata": {},
"outputs": [],
"source": [
"from escpos.printer import Network\n"
]
},
{
"cell_type": "markdown",
"id": "de935eee-c378-4867-b7a4-0753ebb555fc",
"metadata": {},
"source": [
"### functions"
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "f4618f95-9cbb-45a4-ad29-a70febd77fdc",
"metadata": {},
"outputs": [],
"source": [
"# function: respell\n",
"# '''replace all the occurrences of a targeted word in a given text'''\n",
"# def swap(text, target, replacement):\n",
"# result = text.replace(target, replacement)\n",
"# return result\n",
"\n",
"def swap(text, target, replacement):\n",
" target = target.lower()\n",
" txt = word_tokenize(text)\n",
" new = []\n",
" \n",
"# print(f\"swap target:'{target}'\")\n",
" for w in txt:\n",
" if w == target:\n",
" w = replacement\n",
" new = new + [w]\n",
" elif w == target[0:1].upper() + target[1:]:\n",
" w = replacement[0:1].upper() + replacement[1:] \n",
" new = new + [w]\n",
" elif w == target.upper():\n",
" w = replacement.upper()\n",
" new = new + [w]\n",
" else:\n",
" new = new + [w]\n",
" text = ' '.join(new)\n",
" final= text.replace(' .','.').replace(' ,',',').replace(' :',':').replace(' ;',';').replace('< ','<').replace(' >','>').replace(' / ','/').replace('& ','&')\n",
" return final\n",
"\n",
"# function: stitch\n",
"# '''replace all the occurrences of a target word with a single character that is repeated as many times as the length of the target'''\n",
"## example: stitch('halo stitch this', 'this', '*') ----> result:'halo stitch ****'\n",
"def mend(text, target, replacement):\n",
" target = target.lower()\n",
" txt = word_tokenize(text)\n",
" new = []\n",
" \n",
" for w in txt:\n",
" if w == target:\n",
" w = len(w)*replacement\n",
" new = new + [w]\n",
" elif w == target[0].upper() + target[1:]:\n",
" w = len(w)*replacement\n",
" new = new + [w]\n",
" elif w== target.upper():\n",
" w = len(w)*replacement \n",
" new = new + [w]\n",
" else:\n",
" new = new + [w]\n",
" text = ' '.join(new)\n",
" final= text.replace(' .','.').replace(' ,',',').replace(' :',':').replace(' ;',';').replace('< ','<').replace(' >','>').replace(' / ','/').replace('& ','&')\n",
" return final\n",
"\n",
"\n",
"# reveal \n",
"def underline(text,Group):\n",
" txt = word_tokenize(text)\n",
" \n",
" txt_linebr = []\n",
" for token in txt:\n",
" if token == '<':\n",
" continue\n",
" elif token == 'br/':\n",
" token='<br/>'\n",
" txt_linebr.append(token)\n",
" elif token == '>':\n",
" continue\n",
" else:\n",
" txt_linebr.append(token) \n",
" new = []\n",
" group = Group.split(\",\")\n",
" for w in txt_linebr:\n",
" if w=='<br/>':\n",
" new = new + [w]\n",
" elif w not in group:\n",
" w = len(w) * ' '\n",
" new = new + [w]\n",
" elif w in group :\n",
" new = new + [w]\n",
" text = ' '.join(new)\n",
" final= text.replace(' .','.').replace(' ,',',').replace(' :',':').replace(' ;',';').replace('< ','<').replace(' >','>').replace(' / ','/').replace('& ','&')\n",
" return final\n",
"\n",
"#function: txt_br\n",
"#to read the line breaks in a .txt file, returns string readable in html \n",
"def txt_br(any_text):\n",
" lines= any_text.readlines()\n",
" replaced = ''\n",
" for line in lines:\n",
" replaced+= line.replace('\\n', ' <br/> ') \n",
" return replaced"
]
},
{
"cell_type": "markdown",
"id": "29b47df7-5323-41d0-8785-1f992bd38563",
"metadata": {},
"source": [
"### flask "
]
},
{
"cell_type": "markdown",
"id": "4c1b7998-8610-43ba-99a0-6f46b3e433d4",
"metadata": {},
"source": [
"!pwd\n",
"todo: adjust the width of the text on the right"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "70e167cd-929a-4323-9b15-c3926bf28b59",
"metadata": {
"tags": []
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
" * Serving Flask app '__main__' (lazy loading)\n",
" * Environment: production\n",
"\u001b[31m WARNING: This is a development server. Do not use it in a production deployment.\u001b[0m\n",
"\u001b[2m Use a production WSGI server instead.\u001b[0m\n",
" * Debug mode: off\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
" * Running on http://127.0.0.1:9087/ (Press CTRL+C to quit)\n",
"127.0.0.1 - - [17/Dec/2021 11:43:28] \"GET /~grgr/api/and_i_wish_that_your_question_has_been_answered/ HTTP/1.0\" 200 -\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"result is:KYRIAKOS MITSOTAKIS, GREEK PRIME MINISTER <br/> <br/> I understand that in the Netherlands you have a culture of asking direct questions to politicians, which I very much respect. <br/> What I will not accept is that, in this office, you will insult me, or the Greek people, with accusations and expressions that are not supported by material facts when this country has been dealing with a migration crisis of unprecedented intensity, has been saving hundreds, if not thousands of people at sea. <br/> We just rescued 250 people in danger of drowning south of Crete, we are doing this every single day rescuing people at sea, while, at the same time, we are intercepting boats that come from Turkey, as we have the right to do in accordance with European regulations and waiting for the Turkish Coast Guard to come and pick them up and return them to Turkey. <br/> So, rather than putting the blame on Greece, you should put the blame on those who have been instrumentalizing migration systematically pushing people in to a desperate situation from a safe country, because I need to remind you that people who are in Turkey are not in danger, their life is not in danger and you should put the blame on others and not us. <br/> We have a tough, but fair, policy on migration, we have processed and given the right to protection in Greece to 50,000 people, including tens of thousands of Afghans, in accordance… <br/> Allow me. Have you visited the new camps on our islands ? Have you been to Samos ? … No listen to me, you have not been to Samos… No you have not been… <br/> Please…Look, you will not come into this building and insult me. <br/> Am I very clear on this ? <br/> I am answering now and you will not interrupt me, in the same way that I listened to you very carefully. <br/> If you go to Samos, you will find an impeccable camp, with impeccable conditions, funded by EU money, with clean facilities, with playgrounds for…the children to play, no comparison to what we had in the past. <br/> This is our policy, we will stand by it, and I will not accept anyone pointing the finger to this government and accusing it of inhumane behavior. <br/> <br/> <br/> <br/> MARK RUTTE, DUTCH PRIME MINISTER <br/> <br/> I am absolutely convinced that this prime minister and this government is applying the highest standards and the fact that they have immediately launched an investigation on the issue of the pushbacks is testimony of that. <br/> I will now go back on the situation of 2015 and 2016 when we had many people dying on the Aegean Sea trying to get from Turkey into Greece and then to Germany, Sweeden, the Netherlands etc. And I am happy that Germany and we -were holding at that time the rotating presidency of the EU- were able to negotiate the EU and Turkey agreement. <br/> By which indeed Turkey is a safe country for people to stay. <br/> And Turkey at this moment is hosting over 3 million Syrian refugees in the South of Turkey in camps but also in the local communities. <br/> What this country is trying to do is to defend the outer borders of the European Union. <br/> It is a lot of tasks that countries have who are lying on the outside like Italy, Spain, Hungary, Slovenia, but also Poland and Greece, and there is an extremely difficult situation. <br/> What I don t want again is for people to take boats that are not fully equipped to pass the Mediterranean or to pass the Aegean Sea, to die in those circumstances. <br/> I want them to stay there, to be safe, and then we are willing as European Union to take a fair share of people from Africa, from Turkey refugees, in line with the plans devised in 2015 and 2016. <br/> So this is my answer and I wish that your question has been answered.\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"127.0.0.1 - - [17/Dec/2021 11:43:31] \"GET /~grgr/api/archive/ HTTP/1.0\" 200 -\n"
]
}
],
"source": [
"app = Flask(__name__)\n",
"\n",
"\n",
"\n",
"# @app.route(f\"/~grgr/api/\")\n",
"# def replace():\n",
" \n",
"# text=request.values.get('text', '')\n",
"# target = request.values.get('target', '')\n",
"# replacement = request.values.get('replacement', '')\n",
"# title = f\"<h2>{request.values.get('title', '')}</h2>\"\n",
"# txt_result = f\"<p>{swap(text, target, replacement)}</p>\"\n",
"# contents= title + txt_result\n",
" \n",
"# with open(\"./assets/outputs/db.txt\", \"a\") as db:\n",
"# db.write(contents+ \" \\n\")\n",
" \n",
"# with open (\"archive_section.html\", \"a\") as output:\n",
"# print(contents, file=output)\n",
" \n",
"# return render_template(\"index.html\",\n",
"# text=text,\n",
"# contents=contents)\n",
"\n",
"\n",
" \n",
"\n",
"# archive page\n",
"@app.route(f\"/~grgr/api/archive/\", methods=['GET'])\n",
"def archive():\n",
" with open(\"./assets/question.txt\", 'r') as q_file:\n",
" question = txt_br(q_file)\n",
" #read the json file\n",
" with open(\"archive.json\", \"r\") as file:\n",
" archive_input = json.loads(file.read())\n",
" archive = archive_input['archive']\n",
" answers=[]\n",
"# # create a list with all the sections contents from the json\n",
" for section in archive:\n",
" answers.append(section['answers']) \n",
" \n",
" return render_template(\"archive.html\",\n",
" answers = answers,\n",
" question=question)\n",
"\n",
"\n",
"\n",
"# main page with the form:\n",
"temp_changes=[]\n",
"\n",
"@app.route(\"/~grgr/api/and_i_wish_that_your_question_has_been_answered/\", methods=['GET', 'POST'])\n",
"def interact():\n",
" target = request.values.get('target', '')\n",
" f_replacement = request.values.get('f_replacement','')\n",
" function = request.values.get('function', 'replace') \n",
" text = request.values.get('text','')\n",
" result = ''\n",
" \n",
" with open(\"./assets/question.txt\", 'r') as q_file:\n",
" question = txt_br(q_file)\n",
" \n",
" with open(\"./assets/mitsotakis.txt\", 'r') as file:\n",
" source = txt_br(file)\n",
" \n",
" if request.method == 'POST': \n",
" if function == 'mend': \n",
" stitch = f_replacement\n",
" if ' ' in stitch:\n",
" space = stitch.replace(' ','&nbsp; ')\n",
" result= mend(text, target, space)\n",
" else:\n",
" result= mend(text, target, stitch)\n",
" elif function == 'underline':\n",
" popup =underline(source, target)\n",
" result= popup.replace(' ','&nbsp; ')\n",
" else:\n",
" replacement = f_replacement\n",
" result = swap(text, target, replacement)\n",
" print('target is:'+ target)\n",
" # now save temporary all the changes in the list changes\n",
" temp_changes.append(result)\n",
"\n",
" if not text:\n",
" # the first time we open the url the text is not saved, so plz flask, use the original source as text!\n",
" if function == 'mend':\n",
" result = mend(source, target, f_replacement)\n",
" elif function== 'underline':\n",
" popup = underline(source, target)\n",
" result= popup.replace(' ','&nbsp; ')\n",
" \n",
" else:\n",
" result = swap(source, target, f_replacement)\n",
" \n",
" print('result is:'+result)\n",
"\n",
" return render_template('postform.html', question=question, text=text, result=result, source= source, function=function, f_replacement=f_replacement)\n",
"\n",
"\n",
"\n",
"#save the changes to the archive\n",
"@app.route(\"/~grgr/api/save/\", methods=['GET', 'POST'])\n",
"def save_to_archive():\n",
" if request.method == 'POST':\n",
" \n",
" with open(\"./assets/question.txt\", 'r') as q_file:\n",
" question = txt_br(q_file)\n",
" \n",
" with open(\"archive.json\", \"r\") as file:\n",
" archive_input = json.loads(file.read())\n",
" archive = archive_input['archive']\n",
" i = len(temp_changes)-1\n",
" while i >= 0:\n",
" last_change = temp_changes[i]\n",
" if last_change:\n",
" last_change= temp_changes[i]\n",
" \n",
" section = {\"question\":question, \n",
" \"answers\": last_change}\n",
" archive.append(section)\n",
" print('saved')\n",
" break\n",
" else:\n",
" i = i-1 \n",
" \n",
" #print to the archive \n",
" with open(\"archive.json\", \"w\") as file:\n",
" file.write(json.dumps({\"archive\":archive}))\n",
" \n",
" #print with the line printer \n",
"# with open('/dev/usb/lp0', 'w') as lp:\n",
"# print(last_change, file=lp) \n",
"# print_last = last_change.replace('&nbsp; ',' ').replace('<br/>', '\\n')\n",
"# print(f'last print is:{print_last}')\n",
" \n",
" #for ethernet receipt printer\n",
"# kitchen = Network(\"192.168.1.140\") #Printer IP Address\n",
"# kitchen.text(print_last) \n",
" \n",
"# temp_changes.clear()iii\n",
" \n",
" return redirect(\"https://hub.xpub.nl/soupboat/~grgr/api/and_i_wish_that_your_question_has_been_answered/\")\n",
"\n",
"app.run(port=9087)"
]
},
{
"cell_type": "code",
"execution_count": 48,
"id": "6c18d410-7bb3-41a2-a3a6-1b101a7e02be",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"1\n"
]
}
],
"source": [
"temp= ['']\n",
"print(len(temp))"
]
},
{
"cell_type": "markdown",
"id": "aa7e5557-f1eb-40d9-9f7d-a160096dd13c",
"metadata": {},
"source": [
"# "
]
},
{
"cell_type": "markdown",
"id": "35dc1723-75e4-4a21-bb63-6cfcfb84be28",
"metadata": {},
"source": [
"## teeeeeesstttssssss"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "940e6b9f-3645-46a5-a9ae-25f6aa2d5b00",
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "code",
"execution_count": 5,
"id": "540e00da-7e8e-44cf-8b09-7d793e8892c7",
"metadata": {},
"outputs": [],
"source": [
"from weasyprint import HTML, CSS\n",
"\n",
"# with is nice cause it auto-closes the file once \"outside\" the with block\n",
"with open (\"test.html\", \"a\") as output:\n",
" print (\"<pre>\", file=output)\n",
" print (sent + \" \", file=output)\n",
" print (\"</pre>\", file=output)\n",
"HTML(filename=\"test.html\").write_pdf('./test.pdf')"
]
},
{
"cell_type": "code",
"execution_count": 106,
"id": "c1d35ebc-6445-4a94-8f6a-70aa6d39d57c",
"metadata": {},
"outputs": [],
"source": [
"def ifcap(text, target, replacement):\n",
" result=[]\n",
" index= 0\n",
" for w in text.split():\n",
" w_lower = w.lower()\n",
" if w_lower == target and w.isupper()==true:\n",
" while w[index].isupper() == true and index<len(w):\n",
" change=''\n",
" for l in replacement:\n",
" change= l.upper()\n",
" index +=1\n",
" result += [w_lower.replace(target, replacement)]\n",
" print(result)"
]
},
{
"cell_type": "code",
"execution_count": 128,
"id": "613e49a6-874c-4579-b104-02a76224d42b",
"metadata": {},
"outputs": [],
"source": [
"def ifcap(text, target, replacement):\n",
" result=[]\n",
" index= 0\n",
" for w in text.split():\n",
" w_lower = w.lower()\n",
" change=''\n",
" if w_lower == target:\n",
" \n",
" while index < len(w):\n",
" print('oo')\n",
"# if str(w[index]).isupper() == true:\n",
"# change+= str(replacement[index]).upper()\n",
"# print (change)\n",
"# else:\n",
"# change+= replacement[index]\n",
" index+=1\n",
" result += [w_lower.replace(target, change)]\n",
" print(result)"
]
},
{
"cell_type": "code",
"execution_count": 129,
"id": "76967585-28ce-4572-90a4-c7f56cb03d5d",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"['apital', 'and', 'apital']\n"
]
}
],
"source": [
"ifcap('Capital and capital', 'c', 'CC')"
]
},
{
"cell_type": "code",
"execution_count": 130,
"id": "98025f88-ed7b-4976-ab5d-2a8d0fa87112",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"4\n"
]
}
],
"source": [
"word ='ciao'\n",
"print(len(word))"
]
},
{
"cell_type": "raw",
"id": "723542a0-1f53-4f30-a3d1-d492cc3cc078",
"metadata": {},
"source": [
"\n",
"- img\n",
"- multiple targets\n",
"\n",
"\n",
" "
]
},
{
"cell_type": "code",
"execution_count": 13,
"id": "889086dc-1df2-4b9b-ae2a-205a18409a18",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"<_io.TextIOWrapper name='./assets/mitsotakis.txt' mode='r' encoding='UTF-8'>\n"
]
}
],
"source": [
"with open(\"./assets/mitsotakis.txt\", 'r') as source:\n",
" print(source)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "9e5435ae-d71e-4463-b4fd-73a64d37fb59",
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "code",
"execution_count": 44,
"id": "7a35bd62-5909-4ade-b546-4c7f4f0f849e",
"metadata": {},
"outputs": [],
"source": [
"sentence= \"stitches //Have something <\\repairing\\ have HAVE\""
]
},
{
"cell_type": "code",
"execution_count": 17,
"id": "2a2f38f2-18a1-46e1-a39d-bfa75bf780a8",
"metadata": {},
"outputs": [],
"source": [
"# stitch function, replace a target with stitches\n",
"\n",
"def change(text, target, replacement):\n",
" result = text.replace(target, replacement)\n",
" return result"
]
},
{
"cell_type": "code",
"execution_count": 30,
"id": "4b88817c-df89-4b8a-9d64-75e27fb54a06",
"metadata": {},
"outputs": [],
"source": [
"#updated swap function!\n",
"from nltk.tokenize import word_tokenize\n",
"\n",
"def stitch(text, target, replacement):\n",
" target = target.lower()\n",
" txt = word_tokenize(text)\n",
" new = []\n",
" \n",
" for w in txt:\n",
" if w == target:\n",
" w = len(w)*replacement\n",
" new = new + [w]\n",
" elif w == target[0].upper() + target[1:]:\n",
" w = len(w)*replacement\n",
" new = new + [w]\n",
" elif w== target.upper():\n",
" w = len(w)*replacement \n",
" new = new + [w]\n",
" else:\n",
" new = new + [w]\n",
" text = ' '.join(new)\n",
" return text"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "2f868523-f1aa-47d5-872e-c0a224be5b49",
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "code",
"execution_count": 54,
"id": "501d662a-665c-400e-a4b0-954b5fccb4dd",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"'stitches //Have something <epairing\\\\ '"
]
},
"execution_count": 54,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"mend(sentence, 'have', ' ')"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "a4ede71d-2b1c-4158-8520-b7430e52413f",
"metadata": {},
"outputs": [],
"source": [
"\n",
"\n",
"\n",
"\n",
"\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "afaf38e9-8a04-4322-8fda-af3ed5fdc2c9",
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "code",
"execution_count": 15,
"id": "4f86f612-f9ee-49fc-89aa-104afbac62fd",
"metadata": {},
"outputs": [],
"source": [
"source = 'ciao come va'"
]
},
{
"cell_type": "code",
"execution_count": 16,
"id": "984ebd5e-9c56-4627-b287-5669929639b4",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"0\n"
]
}
],
"source": [
"print(source.index('ciao'))"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "4b5db7eb-dac5-496d-8bd5-eaaa6ff2ff2e",
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "code",
"execution_count": null,
"id": "56044dd4-4def-4c44-8042-b53c341e6f64",
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.7.3"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

@ -0,0 +1,37 @@
body {
padding: 16px;
}
.page--header {
width: auto;
top: 45px;
margin: 0;
padding: 0;
}
.sections {
margin-top: 30vh;
display: flex;
flex-direction: column-reverse;
color: var(--textcolor);
row-gap:4vh;
}
.section {
position:relative
}
.reporter {
align-self: flex-start;
width: 70vw;
}
.answer {
align-self: flex-end;
width: 70vw;
text-align: right;
}
@media (max-width: 991.98px) {
.reporter{
width:100%;
}
.answer{
width:100%;
}
}

@ -0,0 +1,7 @@
// Get the button, and when the user clicks on it, execute myFunction
//document.getElementById("info-btn").onclick = function() {showInfo()};
/* myFunction toggles between adding and removing the show class, which is used to hide and show the dropdown content */
function showInfo() {
document.getElementById("info").classList.toggle("show");
}

@ -0,0 +1,270 @@
@font-face {
font-family: Pirelli;
src: url("./font/pirelli-regular.woff");
}
@font-face {
font-family: Pirelli;
src: url("./font/pirelli-bolditalic.woff");
font-weight: bold;
}
@font-face {
font-family: Necto-Mono;
src: url("./font/Necto-Mono.woff");
}
:root {
--red: tomato;
--background: rgb(233, 233, 233);
--textcolor: rgb(39, 39, 39);
--blue: #9edae2;
--pink: #f7d8e8;
--orange: #ffc496;
--yellow: #f9f5b0;
--green: #9fd3a8;
--content-width: 1440px;
--radius: 12px;
--font: "Pirelli";
--title: 36px;
--text: 24px;
--app-margin: 30px;
}
html {
scroll-behavior: smooth;
overflow-x: hidden;
}
body {
font-family: Pirelli;
background-color: var(--background);
color: var(--textcolor);
margin: 0;
}
* {
box-sizing: border-box;
}
a.goto-archive {
position: fixed;
top: 16px;
right: 16px;
color: var(--red);
font-size: 1.2em;
z-index: 1;
}
.page--header {
top: 0;
width: 100%;
margin: 8vh 0;
padding: 0 16px;
display: flex;
flex-direction: row;
column-gap: 8px;
align-items: stretch;
justify-content: center;
position: fixed;
z-index: 1;
}
.page--header > * {
border: solid 1px var(--red);
border-radius: 8px;
padding: 12px;
background-color: var(--background);
}
header {
flex-grow: 1;
}
nav {
cursor: pointer;
}
h1 {
font-size: 36px;
color: var(--red);
margin: 0;
font-weight: normal;
}
.question {
display: flex;
font-size: 1.2em;
flex-direction: column;
flex-grow: 1;
justify-content: space-between;
align-items: flex-start;
row-gap: 24px;
width: 100%;
margin-top: 28vh;
padding: 0 16px;
}
.info {
/*background-color: white;*/
display: none;
padding: 16px;
border: solid 1px var(--red);
border-radius: 8px;
color: var(--red);
font-size: 1em;
flex-basis: 35%;
}
.show {
display: block;
}
.q-text {
margin-bottom: 1em;
flex-basis: 65%;
}
/*styling of form*/
form {
padding: 16px;
display: flex;
flex-flow: wrap;
column-gap: 24px;
row-gap: 16px;
align-items: flex-start;
}
.form-box {
display: flex;
flex-direction: column;
row-gap: 8px;
flex-basis: 30%;
flex-grow: 1;
}
.form-box#apply {
align-self: flex-end;
}
textarea {
resize: none;
/* font-family: Necto-mono; */
border-radius: 4px;
border: solid 1px grey;
transition: border-radius 70ms ease-in-out;
}
/* when the cursor is iside the text area*/
textarea:focus {
border: solid 2px var(--red);
outline: none;
border-radius: 16px;
}
input {
font-family: Pirelli;
width: 100%;
font-size: 1.2em;
margin: 0;
}
input:hover {
color: var(--red);
}
/*to pick the exact type of input you wanna style use "[]"*/
input[type="radio"] {
font-family: Pirelli;
background-color: var(--red);
}
label {
font-size: 0.9em;
}
/*custom radio button*/
/* The container */
.radio-label {
display: block;
position: relative;
padding-left: 35px;
margin-bottom: 12px;
cursor: pointer;
font-size: 1.2em;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
/* Hide the browser's default radio button */
.radio-label input {
position: absolute;
opacity: 0;
cursor: pointer;
}
/* Create a custom radio button */
.checkmark {
position: absolute;
top: 0;
left: 0;
height: 24px;
width: 24px;
background-color: rgb(255, 255, 255);
border: solid 2px var(--red);
border-radius: 50%;
}
/* On mouse-over change color */
.radio-label:hover {
color: var(--red);
}
/* When the radio button is checked, add a blue background */
.radio-label input:checked ~ .checkmark {
background-color: var(--red);
}
/* Create the indicator (the dot/circle - hidden when not checked) */
.checkmark:after {
content: "";
position: absolute;
display: none;
}
.lbl-result {
color: grey;
font-size: 0.8em;
}
.result {
font-family: Courier;
}
.form-end {
margin: 16px;
display: flex;
flex-direction: row;
flex-flow: nowrap;
align-items: center;
align-content: stretch;
column-gap: 8px;
}
.form-end > * {
flex-grow: 1;
flex-basis: 50%;
margin: 0;
padding: 0;
}
a.goto-form {
border: solid 1px rgb(107, 107, 107);
border-radius: 4px;
font-family: Pirelli;
width: 100%;
font-size: 1.2em;
text-decoration: none;
color: var(--textcolor);
text-align: center;
padding: 2px;
}
.goto-form:hover {
color: var(--red);
background-color: rgb(216, 216, 216);
}
.save-btn {
height: 100%;
}
@media (max-width: 991.98px) {
:root {
--app-margin: 18px;
--text: 21px;
}
.title,
nav.h1 {
font-size: 1.5em;
}
}

@ -0,0 +1,48 @@
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" lang="" xml:lang="">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
<title>Archive</title>
<link
rel="stylesheet"
href="/soupboat/si16-app{{url_for('static', filename='font/font.css')}}"
/>
<link
rel="stylesheet"
href="/soupboat/si16-app{{url_for('static', filename='css/variables.css')}}"
/>
<link rel="stylesheet" href="/soupboat/si16-app{{url_for('static', filename='css/mitsotakis/style.css')}}" />
<link rel="stylesheet" href="/soupboat/si16-app{{url_for('static', filename='css/mitsotakis/archive.css')}}" />
<!-- <link rel="stylesheet" href="/soupboat/~grgr/flask{{url_for('static', filename='style.css')}}" /> -->
<link rel="stylesheet" href="/soupboat/si16-app/projects/and-i-wish-that-your-question-has-been-answered{{url_for('static', filename='archive.css')}}" />
</head>
<body>
<a class="goto-archive" href="https://hub.xpub.nl/soupboat/~grgr/api/and_i_wish_that_your_question_has_been_answered/">go to the form ⇝</a>
<section class="page--header">
<header>
<h1>Archive of Repetitive Answers</h1>
<h3>...and I wish that your question has been answered</h3>
</header>
</section>
<!--<button onclick="window.print();" class="noPrint">Print!</button>-->
<div class='sections'>
<!-- {% for section in sections %}
{{section | safe}}
{% endfor %} -->
<!-- {%for answer in answers%}
<div class="answer">{{question | safe}}</div>
<div class="answer">{{answer | safe}}</div>
{%endfor%} -->
{%for answer in answers%}
<div class="answer">{{answer | safe}}</div>
<div class="reporter">{{question | safe}}</div>
{%endfor%}
</div>
</body>
</html>

@ -0,0 +1,133 @@
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" lang="" xml:lang="">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
<title>Mitsotakis/?NO</title>
<link
rel="stylesheet"
href="/soupboat/si16-app{{url_for('static', filename='font/font.css')}}"
/>
<link
rel="stylesheet"
href="/soupboat/si16-app{{url_for('static', filename='css/variables.css')}}"
/>
<link rel="stylesheet" href="/soupboat/si16-app{{url_for('static', filename='css/mitsotakis/style.css')}}" />
<script src="/soupboat/si16-app{{url_for('static', filename='css/mitsotakis/script.js')}}')}}"></script>
</head>
<body>
<a class="goto-archive" href="https://hub.xpub.nl/soupboat/~grgr/api/archive/">go to archive ⇝</a>
<!-- <header>
<h1 id="title">...and I wish that your question has been answered.</h1>
<h1 id="info-btn" onclick="showInfo()">?</h1>
</header> -->
<section class="page--header">
<header>
<h1 class="title" id="title">
...and I wish that your question has been answered.
</h1>
</header>
<nav role="button" id="info-btn" onclick="showInfo()">
<h1>?</h1>
</nav>
</section>
<div class="question">
<div class="info" id="info">
<p>This text is a transcribed excerpt from the Press Conference that followed the meeting between the Greek Prime Minister Kyriakos Mitsotakis and the Dutch Prime Minister Mark Rutte on November the 9th, 2021 in Athens. During the Q&A, the Dutch reporter Ingeborg Beugel asked Mitsotakis for clarity and honesty referring to pushbacks which Greek border guards keep committing towards refugees, while the Greek Government systematically conceals such violence. She continued by asking Mark Rutte what the political stance of the Netherlands towards refugees' relocation and Mitsotakis's policy would be.</p>
<p>The choice of this text as our source material has different reasons. First of all, we were interested in how language can produce categories and shape identities: how does wording create precise borders between the "us" and the "them"?
Our second step would be reflecting on text processing strategies through which a speech or a narration can be recontextualised and reclaimed. By replacing or taking out words of a discourse, and thus making some parts of it interchangeable, we tried to highlight how its phrasing is never neutral, but always a choice led by a particular purpose. </p>
<p>We decided to work on this text as the Press Conference took place at the moment we were developing our research, and as we were really interested on the distinctive rhetoric strategies that Beugel, Mitsotakis and Rutte choose for voicing their goals. It is clear that the reporter uses an emotional and provocative tone to address Mitsotakis' politics, which challenges his composure to a point where he can not keep it anymore, while when talking to Rutte, her speech is more calm and detached.
In response to her question, both Prime Ministers refuse responsibility of their actions: they use a rather managerial and pre-designed language to neutralize the reporter's provocation while at the same time praising the generosity and the efforts of their countries. In particular, Mitsotakis denies any of Beugel's accusations and declares them as unsupported assumptions which is a mere demonstration of power. Alongside, Rutte uses a colder and more restrained language to rationalize the EU and the Greek Government's choices: While shifting the responsibilities for refugee protection, he actually justifies the crimes that are committed within the EU borders as an inevitable "tough, but fair, policy".</p>
<p>Concerning our project, it is an act of persistent resistance. We created a few functions to facilitate an iterative process of refusal towards the two Prime Ministers' answers and any of their possible versions. We invite you to play as much as you want with these functions and create your own answers as counter-reaction to Mark Rutte's final sentence: "So this is my answer and I wish that your question has been answered".
Every new answer, every new iteration, can be submitted to our Archive of Repetitive Answers. Although they will never be good enough, nor shall they be accepted as exhaustive, we consider the modified answers as a trigger for a never-ending dialogue.</p>
</div>
<div class="q-text" id="test">{{question | safe}}</div>
</div>
<div class="interaction" id="change-top">
<form
class="form-get"
action="/soupboat/~grgr/api/and_i_wish_that_your_question_has_been_answered/"
method="POST"
>
<div class="form-box">
<input name="text" type="hidden" value="{{result}}" />
<label for="target"
>type a word you would like to change in the following answer, if
you choose the function "reveal" you can type multiple words, but
separated by commas and with no spaces.
</label>
<textarea name="target" placeholder="target" autofocus></textarea>
<label for="f_replacement"
>type a replacement character or series of words, if you choose the
function "reveal" you don't have to type anything here.</label
>
<textarea
name="f_replacement"
value=""
placeholder="replacement"
></textarea>
</div>
<div class="form-box">
<label for="function">chose the function you want to apply</label>
<label class="radio-label" for="replace"
><input name="function" type="radio" value="replace" id="replace"{%
if function == 'replace'%} checked{%endif%}>respell<span
class="checkmark"
></span
></label>
<label class="radio-label" for="mend"
><input name="function" type="radio" value="mend" id="mend"{% if
function == 'mend'%} checked{%endif%}>stitch<span
class="checkmark"
></span
></label>
<label class="radio-label" for="underline"
><input name="function" type="radio" value="underline"
id="underline"{% if function == 'underline'%}
checked{%endif%}>reveal<span class="checkmark"></span
></label>
</div>
<!-- <label for="replace"><input name="function" type="radio" value="replace" id="replace"{% if function == 'replace'%} checked{%endif%}>respell</label>
<label for="mend"><input name="function" type="radio" value="mend" id="mend"{% if function == 'mend'%} checked{%endif%}>stitch</label>
<label for="underline"><input name="function" type="radio" value="underline" id="underline"{% if function == 'underline'%} checked{%endif%}>reveal</label> -->
<div class="form-box" id="apply">
<input type="submit" value="apply" name="keep" />
</div>
</form>
</div>
<div
class="result"
style="
display: flex;
flex-flow: row;
column-gap: 24px;
width: 70vw;
margin: 0 auto;
"
>
<div style="width: 50%">
<p class="lbl-result"><em>original text</em></p>
<p style="white-space: break-spaces">{{source | safe}}</p>
</div>
<div style="width: 50%">
<p class="lbl-result"><em>modified version</em></p>
<p style="white-space: break-spaces">{{result | safe}}</p>
</div>
</div>
<div class="form-end">
<a class="goto-form" href="#change-top">continue to change the answers</a>
<form action="/soupboat/~grgr/api/save/" method="POST">
<input
type="submit"
value="save to archive"
name="post"
class="save-btn"
/>
</form>
</div>
</body>
</html>

@ -0,0 +1,229 @@
#
#
# DEPENDENCIES
import os
from flask import Flask, url_for, render_template, flash, request, redirect
from werkzeug.utils import secure_filename
import json
import mariadb
import sys
from PIL import Image
#
#
# GLOBAL VARIABLES
UPLOAD_FOLDER = os.path.join(os.getcwd(), 'static/uploads/annotation-compass/')
ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg', 'gif'}
#
#
# FUNCTIONS
def connection():
''' Open a connection to the mariaDB database '''
try:
conn = mariadb.connect(
user="guardian_of_the_labels",
password="soup_of_the_labels",
host="localhost",
port=3306,
database='collecting_labels',
autocommit=True
)
except mariadb.Error as e:
print(f'Error connecting to MariaDB Platflorm: {e}')
sys.exit(1)
return conn
def make_link_list(names, base_url):
''' Generate a list of link from the files in a folder '''
link_list = ''
for name in names:
if allowed_file(name, ALLOWED_EXTENSIONS):
link = f'<a href="{base_url}/{name}/">{name}</a><br/>'
link_list = link_list + link
return link_list
def allowed_file(filename, extensions):
''' Check if the file extension is in the allowed extensions array '''
return '.' in filename and \
filename.rsplit('.', 1)[1].lower() in extensions
def temp_fix_date(timestamp):
''' JS Date to mySQL Date '''
from datetime import datetime
from time import strftime
date = datetime.fromtimestamp(timestamp / 1000.0)
return date.strftime('%Y-%m-%d %H:%M:%S')
def add_label(cursor, label):
''' Insert a new label in the database '''
x = label['position']['x']
y = label['position']['y']
width = label['size']['width']
height = label['size']['height']
text = label['text']
timestamp = temp_fix_date(label['timestamp'])
user_id = str(label['userID'])
image = label['image']
try:
cursor.execute(f'INSERT INTO labels (x,y,width,height,text,timestamp,userID,image) VALUES (?, ?, ?, ?, ?, ?, ?, ?)', (x, y, width, height, text, timestamp, user_id, image ))
print(f'Insert label#{cursor.lastrowid} from user {user_id} on {image} at {timestamp}.')
except mariadb.Error as e:
print(f"Error error error in MariaDB Platform: {e}")
return {"message":"oh no", "error":e}
def get_labels(cursor, filename):
''' List the labels for a specific image from the database '''
try:
cursor.execute(f"SELECT * FROM labels WHERE image = '{filename}'")
db_labels = cursor.fetchall()
labels = []
for (index, x, y, width, height, text, timestamp, user_id, image) in db_labels:
label = {
'position': {
'x': x,
'y': y
},
'size': {
'width': width,
'height': height
},
'text': text,
'timestamp': timestamp,
'userID': user_id,
'image': image
}
labels.append(label)
return labels
except mariadb.Error as e:
print(f"Error error error in MariaDB Platform: {e}")
return {"message":"oh no", "error":e}
def add_image_description(filename, description):
''' Set description for an image during upload '''
image_description_output = {}
with open(os.path.join(os.getcwd(), "projects/annotation-compass/descriptions.json"), "r") as f:
image_description_input = json.loads(f.read())
image_description_output = image_description_input
image_description_output[filename] = {
'description': description
}
with open(os.path.join(os.getcwd(), "projects/annotation-compass/descriptions.json"), "w") as f:
f.write(json.dumps(image_description_output))
def get_image_description(filename):
''' Get the description of an uploaded image '''
with open(os.path.join(os.getcwd(), "projects/annotation-compass/descriptions.json"), "r") as f:
descriptions = json.loads(f.read())
return descriptions[filename]['description']
def thumbnail(image):
try:
img = Image.open(image)
img.thumbnail((128,128))
name = image.rsplit('.', 1)[0]
ext = image.rsplit('.', 1)[1]
img.save(f'{name}_thumb.{ext}')
except IOError:
pass
# TODO: clean up this dai
# /generic-labels/ → /si16/annotation-compass/ (GET, POST)
def upload_file(request):
''' Upload a new image in the Annotation Compass '''
# check if the post request has the file part
if 'file' not in request.files:
flash('No file part')
return redirect(request.url)
file = request.files['file']
# If the user does not select a file, the browser submits an
# empty file without a filename.
if file.filename == '':
flash('No selected file')
return redirect(request.url)
if file and allowed_file(file.filename, ALLOWED_EXTENSIONS):
filename = secure_filename(file.filename)
file.save(os.path.join(UPLOAD_FOLDER, filename))
thumbnail(os.path.join(UPLOAD_FOLDER, filename))
description = request.form['description']
add_image_description(filename, description)
def list_images():
''' List the images in the Annotation Compass. '''
files = os.listdir(UPLOAD_FOLDER)
files.sort()
not_thumbs = filter(lambda file: '_thumb.' not in file, files)
images = []
for image in not_thumbs:
if allowed_file(image, ALLOWED_EXTENSIONS):
name = image.rsplit('.', 1)[0]
ext = image.rsplit('.', 1)[1]
img = image
thumb = f'{name}_thumb.{ext}'
images.append((img, thumb))
return images
# links = make_link_list(images, 'annotate/' )
# TODO: transform this in a template
# return f'''
# <!doctype html>
# <title>Collecting Labels</title>
# {links}
# <h2>Upload new File</h2>
# <form method=post enctype=multipart/form-data>
# <input type=file name=file>
# <input type=submit value=Upload>
# <textarea name=description ></textarea>
# </form>
# '''
# /generic-labels/annotate/<image>/ → /si16/annotation-compass/annotate/<image>/
# def annotate_image(image=None):
# ''' Open the Annotation Compass on a specific image '''
# description = ''
# try:
# description = get_image_description(image)
# except:
# print("There is no description")
# return render_template('annotate_image.html', image=image, description=description)
# /generic-labels/add-label/ → /si16/annotation-compass/add-label
# in the URL there is no reference to the image because the info is in the json body of the label itself
def insert_label(request):
''' Insert a new label in the database '''
connect = connection()
cursor = connect.cursor()
add_label(cursor, request.json)
connect.close()
return {"response": "ok"}
# /generic-labels/get-labels/ → /si16/annotation-compass/get-labels/<image>/
# TODO: adapt the old url with the query
# OLD: /get-labels/?image=<image>
# NEW: /get-labels/<image>/
def get_labels_list(image = None):
connect = connection()
cursor = connect.cursor()
labels = get_labels(cursor, image)
connect.close()
return {"response": "ok", "labels": labels }

File diff suppressed because one or more lines are too long

@ -0,0 +1,129 @@
---
title: "Annotation Compass"
description: "A tool for gathering situated impressions in order to create individual, vernacular and poetic readings of various inputs (such as space, image, text)"
colophon: "Kimberley, Alex, Jian, Emma, Kamo, Supi"
url: "https://hub.xpub.nl/soupboat/si16/annotation-compass/"
url_action: "Annotate"
---
<!-- the contents above are the metadata and the about. this is an example. we can add more parameters if we need them (like a cover image, a link to the project, etc) -->
<!-- ideally there is also a link to the project page in which the real things happen -->
<!-- now this link to the prototype, but later it will link to the right endpoint in the si16 api -->
<!-- the contents below are the showcase -->
<style>
</style>
## About
How do we bring multi-vocality in the work of annotation?
The Annotation Compass builds composites from aggregated vernacular impressions, rich of their subjectivity and situatedness. It is the outcome of a three months journey questioning the relationship between vernacular languages and natural language processing tools. 
## First experiments:
### 1. The living-room:
For this experiment, four of us were gathered in a living-room.
- Number of participants: 4
- Location: Supi's living room
- Aim: Map out each participant's impressions of the living room.
- Material: The living room's floor plan, InDesign, computers.....
- Time-frame: 5 minutes
- Instructions: individually annotate the floor plan with impressions of the living room
After removing the floor plan and looking at the subjective annotations of this experiment, we observed that each outcome forms another 'space'. Each person's set of annotations brings a unique perspective of the living room , an 'individual map'. We then layered the individual maps and the compilation resulted in a vernacular picture of the space. This alternative understanding of the space can only be given to a reader through those descriptions.
<!-- ## Photos of the experiment -->
### 2. Photograph of a room:
The same method was applied to the photograph of a room. Each of us used a different set of coloured sticky notes and took 5 minutes to physically annotate the picture on the same surface. The picture was then removed from the background, resulting in a similar outcome as the experiment described above.
<!-- ## Photos + video of the experiments -->
From these observations, our interest on subjective annotations that could flow in a common understanding of an image grew. As a tool to collect situated impressions, we elaborated the idea of the Annotation Compass.
On a given surface, such as an image, the tool facilitates the collection of annotations and their coordinates from various users simultaneously. These annotations represent individual knowledges and perspectives in regards to the given surface.
## Instructions:
To use this tool, let's consider the "host" any person interested in gathering annotation on a specific image; and the "guest" any person invited by the host to annotate the image.
### Process for the host of an image:
1. upload an image
2. add a text to explain the context of the image or to give instructions and helpful advice to the guests
3. send link to guests and invite them to annotate
4. download a json-file or text-file that contains the collected data that was gathered so far
5. try the different functions of SI16 to filter the collected data
<!-- ## short screen video that shows the process -->
### Process for the guest:
1. open the link sent by the host
2. read the information attached to the image by the host
2. use the cursor to select a specific area that you want to annotate
3. write and insert your annotation(s)a
<!-- ## short screen video that shows the process -->
The data:
The Tool not only archives the annotations, but also additional meta-data that can be helpful to analyze the outcome.
The collected data is stored in a "json-file" that comes as a list of labels. In each label, one can find the file name of the annotated image, the coordinates of the annotation, the dimension of the annotation 'box', the annotation itself, the index number of the annotation and a user identification:
json-file
list of labels
### Example label:
```json
{
'image': 'map.jpg',
'position': {'x': 12, 'y': 97},
'size': {'width': 43, 'height': 18},
'text': 'This is a text! Is this a text?',
'timestamp': 'Wed, 01 Dec 2021 14:04:00 GMT',
'userID': 5766039063
}
```
<!-- ## screenshot of a json-file ehhhmmmmm why not put a link to a real json instead-->
The outcome provided by the Annotation Compass is ever-changing: whenever an individual adds an annotation, the data grows.
After applying the tool to different projects we observed that the collected data can offer a reflexion on the so called "objective": It provides individual perceptions and builds a common experience by including a multiplicity of impressions rather than one objective definition.
In conclusion, the Tool can be used to provide alternative ways to define images, images of space, texts, and anything else annotatable.
## Possible applications of the tool:
- Ask individuals to annotate the space they are in at the moment.
- Ask individuals to annotate a space from memory.
- Ask individuals to annotate imaginary spaces. (e.g. a space from a dream, a fictional space they know from a novel, a place that exists but they never went …)
- Ask individuals to annotate a space before and after they went for the first time.
- Invite individuals to a space and ask them to annotate it as a performative act that is situated not only in space but also time.
- Ask individuals to annotate a space whenever they want (unlimited access).
- Ask individuals to annotate a public space.
- Ask individuals to annotate a whole city, country, continent …
- Ask individuals to annotate a private space.
- Ask individuals to annotate an indoor space (bedroom, library, central station, theatre …)
- Ask individuals to annotate an outdoor space (park, market place, beach …)
- Ask specific groups to annotate a space (queer, teenagers, people with disabilities, immigrants …)
- Ask individuals to annotate specific things, e.g. emotions, colors, surfaces, light …
- Ask individuals to only use specific glyphs (e.g. ! ? and ) or emojis to annotate the space to include those not confident using words.
- Encourage individuals to use their mother tongue / slang / informal language to annotate a space.
- Ask only one individual to give many annotations of a space over time (daily diary, yearly check-in …)
- Ask individuals to annotate different spaces (e.g. their own living rooms)
- Ask individuals to annotate a space without using a standard map but rather an empty sheet as a starting point.
- Ask individuals to annotate a space without using a standard map but rather an individual map or vernacular map as a starting point.
- Ask individuals to annotate a space without using a standard map but rather a photograph of a space as a starting point.
- Ask individuals to annotate a space in real life (e.g. using sticky notes, writing on plexiglass, interview) and use the tool to insert the data afterwards.
- annotate a photograph (portrait, scene, landscape …)
- annotate a painting
- annotate a text
- annotate a song/sound
- misusing the tool

@ -0,0 +1,229 @@
#
#
# DEPENDENCIES
import os
from flask import Flask, url_for, render_template, flash, request, redirect
from werkzeug.utils import secure_filename
import json
import mariadb
import sys
from PIL import Image
#
#
# GLOBAL VARIABLES
UPLOAD_FOLDER = os.path.join(os.getcwd(), 'static/uploads/annotation-compass/')
ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg', 'gif'}
#
#
# FUNCTIONS
def connection():
''' Open a connection to the mariaDB database '''
try:
conn = mariadb.connect(
user="guardian_of_the_labels",
password="soup_of_the_labels",
host="localhost",
port=3306,
database='collecting_labels',
autocommit=True
)
except mariadb.Error as e:
print(f'Error connecting to MariaDB Platflorm: {e}')
sys.exit(1)
return conn
def make_link_list(names, base_url):
''' Generate a list of link from the files in a folder '''
link_list = ''
for name in names:
if allowed_file(name, ALLOWED_EXTENSIONS):
link = f'<a href="{base_url}/{name}/">{name}</a><br/>'
link_list = link_list + link
return link_list
def allowed_file(filename, extensions):
''' Check if the file extension is in the allowed extensions array '''
return '.' in filename and \
filename.rsplit('.', 1)[1].lower() in extensions
def temp_fix_date(timestamp):
''' JS Date to mySQL Date '''
from datetime import datetime
from time import strftime
date = datetime.fromtimestamp(timestamp / 1000.0)
return date.strftime('%Y-%m-%d %H:%M:%S')
def add_label(cursor, label):
''' Insert a new label in the database '''
x = label['position']['x']
y = label['position']['y']
width = label['size']['width']
height = label['size']['height']
text = label['text']
timestamp = temp_fix_date(label['timestamp'])
user_id = str(label['userID'])
image = label['image']
try:
cursor.execute(f'INSERT INTO labels (x,y,width,height,text,timestamp,userID,image) VALUES (?, ?, ?, ?, ?, ?, ?, ?)', (x, y, width, height, text, timestamp, user_id, image ))
print(f'Insert label#{cursor.lastrowid} from user {user_id} on {image} at {timestamp}.')
except mariadb.Error as e:
print(f"Error error error in MariaDB Platform: {e}")
return {"message":"oh no", "error":e}
def get_labels(cursor, filename):
''' List the labels for a specific image from the database '''
try:
cursor.execute(f"SELECT * FROM labels WHERE image = '{filename}'")
db_labels = cursor.fetchall()
labels = []
for (index, x, y, width, height, text, timestamp, user_id, image) in db_labels:
label = {
'position': {
'x': x,
'y': y
},
'size': {
'width': width,
'height': height
},
'text': text,
'timestamp': timestamp,
'userID': user_id,
'image': image
}
labels.append(label)
return labels
except mariadb.Error as e:
print(f"Error error error in MariaDB Platform: {e}")
return {"message":"oh no", "error":e}
def add_image_description(filename, description):
''' Set description for an image during upload '''
image_description_output = {}
with open(os.path.join(os.getcwd(), "projects/annotation-compass/descriptions.json"), "r") as f:
image_description_input = json.loads(f.read())
image_description_output = image_description_input
image_description_output[filename] = {
'description': description
}
with open(os.path.join(os.getcwd(), "projects/annotation-compass/descriptions.json"), "w") as f:
f.write(json.dumps(image_description_output))
def get_image_description(filename):
''' Get the description of an uploaded image '''
with open(os.path.join(os.getcwd(), "projects/annotation-compass/descriptions.json"), "r") as f:
descriptions = json.loads(f.read())
return descriptions[filename]['description']
def thumbnail(image):
try:
img = Image.open(image)
img.thumbnail((128,128))
name = image.rsplit('.', 1)[0]
ext = image.rsplit('.', 1)[1]
img.save(f'{name}_thumb.{ext}')
except IOError:
pass
# TODO: clean up this dai
# /generic-labels/ → /si16/annotation-compass/ (GET, POST)
def upload_file(request):
''' Upload a new image in the Annotation Compass '''
# check if the post request has the file part
if 'file' not in request.files:
flash('No file part')
return redirect(request.url)
file = request.files['file']
# If the user does not select a file, the browser submits an
# empty file without a filename.
if file.filename == '':
flash('No selected file')
return redirect(request.url)
if file and allowed_file(file.filename, ALLOWED_EXTENSIONS):
filename = secure_filename(file.filename)
file.save(os.path.join(UPLOAD_FOLDER, filename))
thumbnail(os.path.join(UPLOAD_FOLDER, filename))
description = request.form['description']
add_image_description(filename, description)
def list_images():
''' List the images in the Annotation Compass. '''
files = os.listdir(UPLOAD_FOLDER)
files.sort()
not_thumbs = filter(lambda file: '_thumb.' not in file, files)
images = []
for image in not_thumbs:
if allowed_file(image, ALLOWED_EXTENSIONS):
name = image.rsplit('.', 1)[0]
ext = image.rsplit('.', 1)[1]
img = image
thumb = f'{name}_thumb.{ext}'
images.append((img, thumb))
return images
# links = make_link_list(images, 'annotate/' )
# TODO: transform this in a template
# return f'''
# <!doctype html>
# <title>Collecting Labels</title>
# {links}
# <h2>Upload new File</h2>
# <form method=post enctype=multipart/form-data>
# <input type=file name=file>
# <input type=submit value=Upload>
# <textarea name=description ></textarea>
# </form>
# '''
# /generic-labels/annotate/<image>/ → /si16/annotation-compass/annotate/<image>/
# def annotate_image(image=None):
# ''' Open the Annotation Compass on a specific image '''
# description = ''
# try:
# description = get_image_description(image)
# except:
# print("There is no description")
# return render_template('annotate_image.html', image=image, description=description)
# /generic-labels/add-label/ → /si16/annotation-compass/add-label
# in the URL there is no reference to the image because the info is in the json body of the label itself
def insert_label(request):
''' Insert a new label in the database '''
connect = connection()
cursor = connect.cursor()
add_label(cursor, request.json)
connect.close()
return {"response": "ok"}
# /generic-labels/get-labels/ → /si16/annotation-compass/get-labels/<image>/
# TODO: adapt the old url with the query
# OLD: /get-labels/?image=<image>
# NEW: /get-labels/<image>/
def get_labels_list(image = None):
connect = connection()
cursor = connect.cursor()
labels = get_labels(cursor, image)
connect.close()
return {"response": "ok", "labels": labels }

File diff suppressed because one or more lines are too long

@ -0,0 +1,129 @@
---
title: "Annotation Compass"
description: "A tool for gathering situated impressions in order to create individual, vernacular and poetic readings of various inputs (such as space, image, text)"
colophon: "Kimberley, Alex, Jian, Emma, Kamo, Supi"
url: "https://hub.xpub.nl/soupboat/si16/annotation-compass/"
url_action: "Annotate"
---
<!-- the contents above are the metadata and the about. this is an example. we can add more parameters if we need them (like a cover image, a link to the project, etc) -->
<!-- ideally there is also a link to the project page in which the real things happen -->
<!-- now this link to the prototype, but later it will link to the right endpoint in the si16 api -->
<!-- the contents below are the showcase -->
<style>
</style>
## About
How do we bring multi-vocality in the work of annotation?
The Annotation Compass builds composites from aggregated vernacular impressions, rich of their subjectivity and situatedness. It is the outcome of a three months journey questioning the relationship between vernacular languages and natural language processing tools. 
## First experiments:
### 1. The living-room:
For this experiment, four of us were gathered in a living-room.
- Number of participants: 4
- Location: Supi's living room
- Aim: Map out each participant's impressions of the living room.
- Material: The living room's floor plan, InDesign, computers.....
- Time-frame: 5 minutes
- Instructions: individually annotate the floor plan with impressions of the living room
After removing the floor plan and looking at the subjective annotations of this experiment, we observed that each outcome forms another 'space'. Each person's set of annotations brings a unique perspective of the living room , an 'individual map'. We then layered the individual maps and the compilation resulted in a vernacular picture of the space. This alternative understanding of the space can only be given to a reader through those descriptions.
<!-- ## Photos of the experiment -->
### 2. Photograph of a room:
The same method was applied to the photograph of a room. Each of us used a different set of coloured sticky notes and took 5 minutes to physically annotate the picture on the same surface. The picture was then removed from the background, resulting in a similar outcome as the experiment described above.
<!-- ## Photos + video of the experiments -->
From these observations, our interest on subjective annotations that could flow in a common understanding of an image grew. As a tool to collect situated impressions, we elaborated the idea of the Annotation Compass.
On a given surface, such as an image, the tool facilitates the collection of annotations and their coordinates from various users simultaneously. These annotations represent individual knowledges and perspectives in regards to the given surface.
## Instructions:
To use this tool, let's consider the "host" any person interested in gathering annotation on a specific image; and the "guest" any person invited by the host to annotate the image.
### Process for the host of an image:
1. upload an image
2. add a text to explain the context of the image or to give instructions and helpful advice to the guests
3. send link to guests and invite them to annotate
4. download a json-file or text-file that contains the collected data that was gathered so far
5. try the different functions of SI16 to filter the collected data
<!-- ## short screen video that shows the process -->
### Process for the guest:
1. open the link sent by the host
2. read the information attached to the image by the host
2. use the cursor to select a specific area that you want to annotate
3. write and insert your annotation(s)a
<!-- ## short screen video that shows the process -->
The data:
The Tool not only archives the annotations, but also additional meta-data that can be helpful to analyze the outcome.
The collected data is stored in a "json-file" that comes as a list of labels. In each label, one can find the file name of the annotated image, the coordinates of the annotation, the dimension of the annotation 'box', the annotation itself, the index number of the annotation and a user identification:
json-file
list of labels
### Example label:
```json
{
'image': 'map.jpg',
'position': {'x': 12, 'y': 97},
'size': {'width': 43, 'height': 18},
'text': 'This is a text! Is this a text?',
'timestamp': 'Wed, 01 Dec 2021 14:04:00 GMT',
'userID': 5766039063
}
```
<!-- ## screenshot of a json-file ehhhmmmmm why not put a link to a real json instead-->
The outcome provided by the Annotation Compass is ever-changing: whenever an individual adds an annotation, the data grows.
After applying the tool to different projects we observed that the collected data can offer a reflexion on the so called "objective": It provides individual perceptions and builds a common experience by including a multiplicity of impressions rather than one objective definition.
In conclusion, the Tool can be used to provide alternative ways to define images, images of space, texts, and anything else annotatable.
## Possible applications of the tool:
- Ask individuals to annotate the space they are in at the moment.
- Ask individuals to annotate a space from memory.
- Ask individuals to annotate imaginary spaces. (e.g. a space from a dream, a fictional space they know from a novel, a place that exists but they never went …)
- Ask individuals to annotate a space before and after they went for the first time.
- Invite individuals to a space and ask them to annotate it as a performative act that is situated not only in space but also time.
- Ask individuals to annotate a space whenever they want (unlimited access).
- Ask individuals to annotate a public space.
- Ask individuals to annotate a whole city, country, continent …
- Ask individuals to annotate a private space.
- Ask individuals to annotate an indoor space (bedroom, library, central station, theatre …)
- Ask individuals to annotate an outdoor space (park, market place, beach …)
- Ask specific groups to annotate a space (queer, teenagers, people with disabilities, immigrants …)
- Ask individuals to annotate specific things, e.g. emotions, colors, surfaces, light …
- Ask individuals to only use specific glyphs (e.g. ! ? and ) or emojis to annotate the space to include those not confident using words.
- Encourage individuals to use their mother tongue / slang / informal language to annotate a space.
- Ask only one individual to give many annotations of a space over time (daily diary, yearly check-in …)
- Ask individuals to annotate different spaces (e.g. their own living rooms)
- Ask individuals to annotate a space without using a standard map but rather an empty sheet as a starting point.
- Ask individuals to annotate a space without using a standard map but rather an individual map or vernacular map as a starting point.
- Ask individuals to annotate a space without using a standard map but rather a photograph of a space as a starting point.
- Ask individuals to annotate a space in real life (e.g. using sticky notes, writing on plexiglass, interview) and use the tool to insert the data afterwards.
- annotate a photograph (portrait, scene, landscape …)
- annotate a painting
- annotate a text
- annotate a song/sound
- misusing the tool

@ -0,0 +1,98 @@
---
title: Cloverleaf
author: Kimberley
description: Opening short-cuts between texts
functions: clean_word(), bridge(), bridge_list(),
template:
---
## Description:
Cloverleaf is a tool to navigate a set of text. Through generated short-cuts, it is meant to interrupts the linearity of a text. The result is a collage of excerpts and aims to free unexpected reading paths.
The tool can be used to stitch various voices together in a non-hierarchical manner, giving off hybrid constructions where common points and divergences can co-exist.
## Picture:
![cloverleaf](soupboat/shared/html/si16-app/static/img/cloverleaf/cloverleaf.jpeg)
(Picture of a cloverleaf)
## In detail:
For two texts in a set of text following an index order, lets consider a preceding text and its succeeding. In a first place, the function bridge() will look for the first identical word occurring in both texts (excluding stop words). Lets name the position (index) of this word i for the preceding text and j for the succeeding text.
example (change later with example from the corpus):
text, index 0: “Strawberries dont grow tasty (i) in the Netherlands.”
text, index 1: “Pineapple is very tasty (j) with salt (i) and chilli powder.”
text, index 2: “Blocks of salt (j) distract cows (i).”
text, index 3: “There was many field with cows (j) in this area.”
Since every text, within a given set of at least four texts, will alternatively take the preceding and the succeeding position, each text will hold a word indexed as i and a word indexed as j: marking the identical words occurring between a text and its succeeding. These marks will then determine the start and the end of each excerpt, and open the shortcut aforementioned.
As a result, the preceding text will be printed from its index j—attributed formerly when this text was in a succeeding position—until i, its common word with its current succeeding text. The function will loop until the last two texts of the set (in the index order).
TEXT 1 xxxxxxxxxJooooooooooooIxxxxxxxxxxxxx
TEXT 2 xxxxxxxxxxxxxxxxxJooooooooooooIxxxxxxxxxx
TEXT 3 xxxxxxxxxxxJoooooooIxxxxxxxxxxxxxxxx
TEXT 4 xxxxxxJooooooooooooooooIxxxxxxxxxxxxxx
o = printed text
x = rejected text
J = same word's index preceding text
I = same word's index in suceeding text
In the case no match is found between two texts, the text in succeeding position will be printed from its first word to its last.
## Example drawing:
(Image here!)
## Example with 3 excerpts from text corpus on orientation/navigation:
(text here)
## Cloverleaf and the Annotation Compass:
Cloverleaf was imagined to navigate the annotations gathered with the Annotation Compass and offers the possibility to process the ever-growing data in the form of a json file. Used in complementarity, they become a fertile collective writing environment.
(Link to Annotation Compass 'about' page)
In a first experiment, a group of eight persons was given the following instruction:
## Instructions:
For as long as one minute; you are kindly asked to define, in your own words and logic, the verb on the screen. This definition does not necessarily have to have sense for anyone else but you, although English language will be our common ground in this experiment.
Important: You are required to write for the entire span of this granted minute! Do not lift hands from keyboard, and in case of blockage you are welcome to press any key or write any word, onomatopoeia, etc.
For this experiment, each participant will only fill in one insert box per word to be defined.
## George Perec excerpt:
![perec](soupboat/shared/html/si16-app/static/img/cloverleaf/perec.jpeg)
## Participants texts:
(Connect with Json 'text')
1.To arrange:
"...."
"...."
"...."
"...."
2.cccc ...
## Button?:
Press here to see the result. (Text black with colloured highlights)
(Here, button to bridge the previous texts together)
## Try now!:
Contribute: After getting acquainted with the tool and the instruction above, go to the Annotation Compass and insert your annotations on one, several or all the words proposed:
(Here, button/links to Annotation Compass x14)
You can also update your own surface for collective annotating
##

@ -0,0 +1,7 @@
---
title: Infiltrating the Filter
author: Supisara
colophon: A showcase of the 'Annotation Compass'
description: Alternative annotation approaches
template:
---

@ -0,0 +1,65 @@
---
title: the impossible process of grasping the other side
author: flem
colophon: Emma Prato
description: how does a translator choose words? why do they choose a instead of b? c instead of a? not all translations are perfect so then, why not mix them up?
url: "https://hub.xpub.nl/soupboat/~flem/api/translations/mashup/"
---
##
how does a translator choose words? <br> why do they choose a instead of b? c instead of a? <br>
not all translations are perfect so then, why not mix them up?
## introduction
<b>the impossible process of grasping the other side </b> is a project by Emma Prato, as part of the Special Issue 16 //Learning How to Walk While Catwalking// carried out by Piet Zwart Institute (XPUB) students on the topic of Vernacular Language Processing.
the impossible process basically wants to play around with the structure of poetry translations and mix together different versions of the same text.
translating a poem into another language is an impossible process - so what if we gather translations from different people? from different languages? and what if then we mix them up? what if we change them? what if we play with words? it probably won't solve the impossible process but for sure is an interesting experiment.
<i>//multiple translations can give us a much better sense of the poem than a single translation can, so that even if we cant read the poem in the original language, we can come closer to that experience//</i>
<b>the impossible process</b> is made of two sections: one (<b>THE CROWD</b>) is to gather //vernacular// information and the other (<b>THE MASHUP</b>) is to process texts, which can be both taken from the vernacular content or from already existing translations. The poetry archive (work in progress) will showcase the results.
## the impossible process creates processed vernacular translations. what does it mean?
//vernacular// means made by humans for humans, vernacular is the material I am gathering through the Annotation Compass°, a place where everyone can have access to, where everyone can give their contribution, regardless their background or knowledge etc.°
//processed// means that the content gathered through humans' actions is transformed by the machine to create something new.
what we are gathering, what we are processing, everything is //language//. in the case of the impossible process, we are using translations, in particular translations of poems.
## the impossible process THE MASHUP // USE THE MASHUP() FUNCTION
the impossible process THE MASHUP tries to approach the impossible process of translation by the use of a Python function that:
1. takes into account two different translations of a poem
2. finds the common words and uses them as the fixed text for the new piece of text
3. puts the results together into a new piece, randomly choosing the differences in the two texts
The resulting text will show how many different ways there are to translate a text, a line, a concept, a single word.
the impossible process is a funny way to highlight the thousand facets of a translation and create // never-ending // new texts randomly choosing the differences.
Here you will find the space to compare your own texts yourself! Just click on the button below, access the mashup() function and start to create random poems!
## the impossible process THE CROWD // CONTRIBUTE TO THE IMPOSSIBLE PROCESS
the impossible process THE CROWD consists into three annotation methods, in order to gather different material from users, then to process the texts and create new vernacular content.
The results will be part of the Archive. If you want to contribute to the Archive of vernacular translations (or experimental texts), choose a method, access the image, insert your translation and wait for your text to be used.
.one to one translation starts from a poem in x language to another x language (for example from Italian to English)
.many to one translation starts from different language translations of the same original poem. Users can choose the language they prefer to start from and then insert the translation to english (//common language) through the Annotation Compass.
.free fun translation starts from a common language and lets users interact as they want, without restrictions.
## the impossible process ARCHIVE OF VERNACULAR POEMS
the purpose of this archive is divalent: the first is to gather vernacular translations from users around the world, and to fill this archive with translations that don't exist yet -- the second is to create always new random versions of already existing translations, or of vernacular translations, to play around with language and words.

@ -0,0 +1,144 @@
---
title: Rejection Map — A Situated Topography of Rejection
author: Jian
colophon: Jian
description: How does a translator choose words? First description of the project
functions: individual_map, vernacular_map, area_map, target_map, highlight_map, ghost_map, html_tag
template: rejection_map (6 interactive forms need to be implemented, for placeholders scroll down)
---
## Rejection Map
## A Situated Topography of Rejection
A city is a shared space for individuals with diverse backgrounds, identities and ways to perceive the world around them. Probably all of them have something in common: at some point they experienced a moment of rejection. These moments of rejection can take place in various contexts. People experience rejection in private relationships or in society; in the context of work or school; in relation to their body or appearance, their gender, identity or orientation; referring to their language or nationality; when it comes to housing or navigating in the city or even the supermarket …
Rejection can also be a very personal and much lighter experience: the moment you finally let go of a bad thought; the moment you make an independent choice that is free from other's opinions; the moment you skip the party and have an evening for yourself.
In any case, rejection resonates an intimate level; a situated, embodied experience that stays with you. Many people will be able to remember those moments and even locate them on a map. And more often than not a moment of rejection shapes the individual but also the way they perceive the landscape and their surroundings.
The multiplicity of subjective, embodied and situated experiences of rejection is a way to acknowledge and embrace different ways of knowing and feeling. All these singularities dont stand isolated or alone. They are memories or anecdotes, traces or echoes of rejection. They relate, interact and overlap. Organically and ever-changing they form new maps of the city and being together on these new maps, sharing these incommensurable experiences maybe offers a sense of community and a chance for healing.
Maps are meaningful. As companions in everyday life or while travelling to unknown places they help us to orientate and navigate the world. And at the same time maps themselves represent some and reject others. Maps have the power to shape the world and to produce realities. For this collaborative and collective mapping project, people are asked to situate and describe their experiences of rejection in Rotterdam. On a map of the city they can select an area and use their own words to express their perceptions on.
Language, used to record and share human experiences, can be very powerful. We grow up using language as a way to communicate with other human beings and our own way of phrasing things is in many cases deeply connected to our identity. Especially written language adds a layer of consciousness to this: A moment of listening to yourself, before thoughtfully choosing words that best express a very intimate thought or feeling.
In what way do textual annotations redefine the notion of a map and how we relate to it?
In this alternative approach to mapping, the different perspectives on space are not only part of a research and data collecting process, but they actually create maps of their own through these linguistic annotations of affective relations. The Annotation Compass is a tool that helps to gather these particular knowledges, that altogether reveal both the commonalities and the differences of this juxtaposition. This collective approach does not necessarily aim to create a counter-map. However, it can be seen as an unfamiliar gesture that brings up questions about how, by whom and for whom maps are made. The lines between mapping and being mapped become fluid.
## How to become part of the Rejection Map
![Map of Rotterdam © OpenStreetMap contributors](/soupboat/si16-app/static/img/rejection_map/rejection_map.jpg))
[Annotation Compass Rejection-Map of Rotterdam](https://hub.xpub.nl/soupboat/generic-labels/annotate/rejection_map.jpg/)
You are invited to share your experiences with rejection in the city of Rotterdam. Please be aware that your descriptions will be visible for others to read. Your contribution is voluntary and anonymous: you decide what moments and details you want to share and there is no data collected that traces back to you. As rejection is a sensitive topic, you should only share intimate things if you feel comfortable and safe. When sharing moments that involve other individuals, please be mindful that they might not want to be exposed.
1. Open the link to the Annotation Compass. You can see the map of Rotterdam
2. Look at the map and try to think of a moment of rejection you experienced in a certain area in Rotterdam
3. Use your cursor to select this specific area of the map where you can insert your first experience. It can be a quite small area (if you want to select a building) or a bigger one (if your experience includes a whole neighborhood).
4. Please choose your own way to describe your moment of rejection. It can be short or long; formal or informal; personal or distanced; poetic or pragmatic; cryptic or explicit; anecdotal or out of context; using vernacular or academic language, slang or mother tongue; using whole sentences, single words, single letters or punctuation only … Do whatever you feel most comfortable with.
5. Click ”insert“ to save your first description or ”x“ if you want to delete it.
6. Follow the same steps to add more moments of rejection if you like. If several moments relate to the same space or area, they can overlap.
Thank you for sharing!
## Individual Maps
By adding annotations to the Rejection Map, each individual creates an Individual Map of Rejection: It displays all moments of rejection that were shared by one person. Each time someone decides to join the project, a new Individual Map is created. These maps are diverse in their appearance. Some share several experiences with rejection, others only one. Some focus on one area, while others show annotations all over the city. Especially the personal experience with rejection and the way it is described are diverse and vary from person to person. The Individual Maps validate these intimate moments that were shared.
Looking through all the Individual Maps almost feels like reading a collective diary, where the reader is encouraged to change perspectives and to emphasize with each individual.
====== interactive form ======
## Vernacular Map
The Vernacular Map combines all Individual Maps into one collective map by layering moments of rejection shared by individuals in the city of Rotterdam. While each of them is unique, they overlap and connect and whenever an Individual Map is added, the Vernacular Map changes as well. It illustrates that in our individualities we still share experiences and also space.
====== interactive form ======
## Area Map
The Area Map offers a different way to read and navigate the Rejection Map by focusing on a specific area of Rotterdam. The parameters can be adjusted to look at all annotations in a certain neighborhood or any area area of interest.
====== interactive form ======
## Target Map
The Target Map offers a different way to read and navigate the Rejection Map by filtering for one or more specific target words. The parameters can be adjusted to look at all annotations that include these targets.
====== interactive form ======
## Highlight Map
The Highlight Map offers a different way to read and navigate the Rejection Map by filtering for one specific target word and highlighting all annotations that include this target. The parameters can be adjusted.
====== interactive form ======
## Ghost Map
Sometimes descriptions of rejection can be very explicit, hurtful or triggering. The Ghost Map replaces all characters in the annotations with a specific glyph. The moments of rejection become abstract: The Ghost Map shows remnants, traces or echoes of individual experiences that are not always present, but imprinted in the memories of the individual and the city.
====== interactive form ======
## Archive
[Collective Rejection Glossary](https://pad.xpub.nl/p/Rejection_Glossary/)
![Experiment 01](/soupboat/si16-app/static/img/rejection_map/vernacular-map-experiments-01.jpg)
![Experiment 02](/soupboat/si16-app/static/img/rejection_map/vernacular-map-experiments-02.jpg)
![Experiment 03](/soupboat/si16-app/static/img/rejection_map/vernacular-map-experiments-03.jpg)
![Experiment 04](/soupboat/si16-app/static/img/rejection_map/vernacular-map-experiments-04.jpg)
![Experiment 05](/soupboat/si16-app/static/img/rejection_map/vernacular-map-experiments-05.jpg)
![Experiment 06](/soupboat/si16-app/static/img/rejection_map/vernacular-map-experiments-06.jpg)
![Experiment 07](/soupboat/si16-app/static/img/rejection_map/vernacular-map-experiments-07.jpg)
![Experiment 08](/soupboat/si16-app/static/img/rejection_map/vernacular-map-experiments-08.jpg)
![Experiment 09](/soupboat/si16-app/static/img/rejection_map/vernacular-map-experiments-09.jpg)
![Experiment 10](/soupboat/si16-app/static/img/rejection_map/vernacular-map-experiments-10.jpg)
![Experiment 11](/soupboat/si16-app/static/img/rejection_map/vernacular-map-experiments-11.jpg)
![Experiment 12](/soupboat/si16-app/static/img/rejection_map/vernacular-map-experiments-12.jpg)
![Experiment 13](/soupboat/si16-app/static/img/rejection_map/vernacular-map-experiments-13.jpg)
![Experiment 14](/soupboat/si16-app/static/img/rejection_map/vernacular-map-experiments-14.jpg)
![Experiment 15](/soupboat/si16-app/static/img/rejection_map/vernacular-map-experiments-15.jpg)
![Experiment 16](/soupboat/si16-app/static/img/rejection_map/vernacular-map-experiments-16.jpg)
![Experiment 17](/soupboat/si16-app/static/img/rejection_map/vernacular-map-experiments-17.jpg)
![Experiment 18](/soupboat/si16-app/static/img/rejection_map/vernacular-map-experiments-18.jpg)
![Experiment 19](/soupboat/si16-app/static/img/rejection_map/vernacular-map-experiments-19.jpg)
![Experiment 20](/soupboat/si16-app/static/img/rejection_map/vernacular-map-experiments-20.jpg)
![Experiment 21](/soupboat/si16-app/static/img/rejection_map/vernacular-map-experiments-21.jpg)

@ -0,0 +1,103 @@
---
title: Rotterdam Impressions
author: Ål Nik (Alexandra Nikolova)
colophon: A showcase of the 'Annotation Compass'
description: A collective map of Rotterdam impressions, annotated by the first year Experimental Publishing students
functions: Adjectives, Nouns filers, Most common words filter
---
## Introduction
### rotterdam impressions with the 'annotation compass'
We all arrived here from different places, backgrounds, histories and languages. As a group, we are building our relationships and exploring our personalities. Brought to this new place, we discover it both individually and collectively. The city is our everyday environment - we navigate in it to go to school, to go home, to do our grocery, to have a drink out. We spend some of these moments alone, sometimes with someone else. In a short moment of reflection, we all remembered our first or strongest impressions of some particular areas of Rotterdam, which triggered a memorable emotion in us.
These illustrated subjective maps represent the impressions of the first year master students of Experimental Publishing at the Piet Zwart Institute. Their situated experiences trigger various ways to describe their new home: Rotterdam. They were invited to reflect on the way they remember some of the areas in the city and to describe how they feel about them. In words, in their own vernacular English - the common language that is not native to anyone of them but the main language they communicate between each other. Afterwards, their descriptions were filtered by a Python function to extract the most common words they used whilst describing their perceptions of the selected areas on the map, divided in three main categories - adjectives, nouns and pronouns. The final outcome is this short list of the most common impressions of various places and illustrated maps as an artistic expression of the author.
### the most common...
In the following lists we observe the most common words the annotators used in their descriptions. They were asked to describe how they feel about areas and places they see on the map which trigger some memories and emotions in them. By using the Python filters for extracting the words that are adjectives (the words people used to describe their impressions of Rotterdam and various areas and places in it), nouns (the objects that we see people referring to) and pronouns (they way the approached the reflection) in combination with a filter for the most common words (which shows us which words are used the most and how many times), we can observe what are the feelings and impressions that they share as a new collective. What are the places and emotions that are common for some of them?
```python
[('I', 53), ('my', 21), ('we', 19), ('me', 16), ('i', 13), ('place', 9), ('super', 9), ('We', 9), ('happy', 8), ('excited', 8), ('our', 8), ('were', 8), ('beautiful', 7), ('Rotterdam', 7), ('first', 6), ('area', 6), ('calm', 5), ('nervous', 5), ('big', 5), ('fig', 5), ('tree', 5), ('safe', 4), ('home', 4), ('inspired', 4), ('city', 4), ('little', 4), ('room', 4), ('shocked', 4), ('fun', 3), ('frustrated', 3), ('friendly', 3), ('good', 3), ('best', 3), ('bridge', 3), ('school', 3), ('nice', 3), ('station', 3), ('weird', 3), ('sad', 3), ('bike', 3), ('neighborhood', 3)]
```
![Screenshot of the result](/soupboat/si16-app/static/img/rotterdam_impressions/map_00.jpg)
### vernacular map
The visual maps of [rotterdam impressions] depict how many times specific words were mentioned in the descriptions overall - the positions of the words are not related to the specific areas where they were mentioned because they are a collective print of how many times these words were used anywhere on the map. By printing each time a word was used, we can see the intensity of words used by the annotators. They are coloured due to their category - adjectives are in green, nouns are in orange and pronouns - in pink.
![Screenshot of the result](/soupboat/si16-app/static/img/rotterdam_impressions/map_01.jpg)
### how are we describing
"I" and "we" are the most common pronouns (in pink) that were used in the annotations. You can see that "I" was used as twice more often that "we", but perhaps because of the context of us describing our first impressions of a new place where everyone arrived to study and work together as a group, some of the experiences were still connected to a collective moment.
![Screenshot of the result](/soupboat/si16-app/static/img/rotterdam_impressions/map_03.jpg)
### adjectives as emotions and impressions; nouns as objects
In the context of the annotation compass, the adjectives (in green) were filtered and extracted as impressions and descriptions of the new place where people arrived. They are either describing Rotterdam and places inside it, or the way the annotator feels about them. Either way, in most of the cases we can still get the emotion that lays behind the word used. The most common nouns are understood as the objects (in orange) - the ones people are relating most often to.
![Screenshot of the result](/soupboat/si16-app/static/img/rotterdam_impressions/map_04.jpg)
![Screenshot of the result](/soupboat/si16-app/static/img/rotterdam_impressions/map_02.jpg)
## Functions
To filter the adjectives from the annotations:
```
def adjectives(text):
adj = []
for (word, tag) in labels_blob.tags:
if tag == 'JJ':
adj = adj + [word]
return adj
```
To filter the nouns from the annotations:
```
def nouns(text):
noun = []
for (word, tag) in labels_blob.tags:
if tag == 'NN':
noun = noun + [word]
return noun
```
To filter the most common words from the annotations:
```
def most_common(words):
freq_dist_pos = FreqDist(words)
return freq_dist_pos.most_common(100)
```
## Process
Getting to the final showcase of the "annotation compass" was a long way of going back and forth whilst learning my first steps in Python. From the beginning my idea was to create vernacular map of our rotterdam impressions using our words in describing emotions and experineces. Here is what happened on the road:
### 0. background story
In May 2021 I went through a massive burnout which resulted in me going to the hospital and not being able to climb the stairs at home normally for a week, because I couldn't take a deep breath. This traumatic experience got me started paying more attention to the signs of me getting overwhelmed; and also since then I have been living in fear of going through a similar experience again.
After that crazy time, I had to organise my moving to Rotterdam and do some work on the way around Europe. After a year in isolation because of the pandemic, this was a dramatic change and I was afraid I might have another panic attack whilst being alone in the train in the middle of nowhere. In order to keep a track of my emotions and the way I feel, I started mapping them. That experiment made me think more of how to use our "annotation compass" to map our group's experiences and feelings.
![Screenshot of the result](/soupboat/si16-app/static/img/rotterdam_impressions/prototype_00.jpg)
### 1. ideas & prototypes
The initial idea was to try mapping our emotions when reflecting on our first experiences in Rotterdam. In order to visualise that, I made a few prototypes by me mapping the places I feel most strongly about and then doing the same with my colleague Supisara.
The prototypes were adjectives put on the map of Rotterdam, describing how we felt about a specific place or area. Later on, I figured it would be much more interesting to give freedom to the annotator to choose how they want to describe their perceptions. And after that, I could filter their texts in order to extract the adjectives.
![Screenshot of the result](/soupboat/si16-app/static/img/rotterdam_impressions/prototype_01.jpg)
### 2. collecting annotations
We used the "annotation compass" to write down some reflections about our arrival in Rotterdam and the way we feel about some places and areas. The invitation to the group was to take a few minutes to reflect on their arrival in the city and they feel strongly on some areas and spaces; and to describe the way they feel about them.
![Screenshot of the result](/soupboat/si16-app/static/img/rotterdam_impressions/prototype_02.jpg)
### 3. filtering the annotations with python
Since there was no restriction what words the use in the descriptions, I applied a combination of Python functions in order to process the data and extract the most common words. I also looked up for how many times the most common adjectives, nouns and pronouns were used. Looking at the result of the text processing, I created illustrated maps with the number of times these most common words were used.

@ -0,0 +1,98 @@
---
title: Cloverleaf
author: Kimberley
description: Opening short-cuts between texts
functions: clean_word(), bridge(), bridge_list(),
template:
---
## Description:
Cloverleaf is a tool to navigate a set of text. Through generated short-cuts, it is meant to interrupts the linearity of a text. The result is a collage of excerpts and aims to free unexpected reading paths.
The tool can be used to stitch various voices together in a non-hierarchical manner, giving off hybrid constructions where common points and divergences can co-exist.
## Picture:
![cloverleaf](soupboat/shared/html/si16-app/static/img/cloverleaf/cloverleaf.jpeg)
(Picture of a cloverleaf)
## In detail:
For two texts in a set of text following an index order, lets consider a preceding text and its succeeding. In a first place, the function bridge() will look for the first identical word occurring in both texts (excluding stop words). Lets name the position (index) of this word i for the preceding text and j for the succeeding text.
example (change later with example from the corpus):
text, index 0: “Strawberries dont grow tasty (i) in the Netherlands.”
text, index 1: “Pineapple is very tasty (j) with salt (i) and chilli powder.”
text, index 2: “Blocks of salt (j) distract cows (i).”
text, index 3: “There was many field with cows (j) in this area.”
Since every text, within a given set of at least four texts, will alternatively take the preceding and the succeeding position, each text will hold a word indexed as i and a word indexed as j: marking the identical words occurring between a text and its succeeding. These marks will then determine the start and the end of each excerpt, and open the shortcut aforementioned.
As a result, the preceding text will be printed from its index j—attributed formerly when this text was in a succeeding position—until i, its common word with its current succeeding text. The function will loop until the last two texts of the set (in the index order).
TEXT 1 xxxxxxxxxJooooooooooooIxxxxxxxxxxxxx
TEXT 2 xxxxxxxxxxxxxxxxxJooooooooooooIxxxxxxxxxx
TEXT 3 xxxxxxxxxxxJoooooooIxxxxxxxxxxxxxxxx
TEXT 4 xxxxxxJooooooooooooooooIxxxxxxxxxxxxxx
o = printed text
x = rejected text
J = same word's index preceding text
I = same word's index in suceeding text
In the case no match is found between two texts, the text in succeeding position will be printed from its first word to its last.
## Example drawing:
(Image here!)
## Example with 3 excerpts from text corpus on orientation/navigation:
(text here)
## Cloverleaf and the Annotation Compass:
Cloverleaf was imagined to navigate the annotations gathered with the Annotation Compass and offers the possibility to process the ever-growing data in the form of a json file. Used in complementarity, they become a fertile collective writing environment.
(Link to Annotation Compass 'about' page)
In a first experiment, a group of eight persons was given the following instruction:
## Instructions:
For as long as one minute; you are kindly asked to define, in your own words and logic, the verb on the screen. This definition does not necessarily have to have sense for anyone else but you, although English language will be our common ground in this experiment.
Important: You are required to write for the entire span of this granted minute! Do not lift hands from keyboard, and in case of blockage you are welcome to press any key or write any word, onomatopoeia, etc.
For this experiment, each participant will only fill in one insert box per word to be defined.
## George Perec excerpt:
![perec](soupboat/shared/html/si16-app/static/img/cloverleaf/perec.jpeg)
## Participants texts:
(Connect with Json 'text')
1.To arrange:
"...."
"...."
"...."
"...."
2.cccc ...
## Button?:
Press here to see the result. (Text black with colloured highlights)
(Here, button to bridge the previous texts together)
## Try now!:
Contribute: After getting acquainted with the tool and the instruction above, go to the Annotation Compass and insert your annotations on one, several or all the words proposed:
(Here, button/links to Annotation Compass x14)
You can also update your own surface for collective annotating
##

@ -0,0 +1,9 @@
---
title: Infiltrating the Filter
author: Supisara
colophon: A showcase of the 'Annotation Compass'
description: Alternative approaches to annotating
template:
---
## Introduction

@ -0,0 +1,65 @@
---
title: the impossible process of grasping the other side
author: flem
colophon: Emma Prato
description: how does a translator choose words? why do they choose a instead of b? c instead of a? not all translations are perfect so then, why not mix them up?
url: "https://hub.xpub.nl/soupboat/~flem/api/translations/mashup/"
---
##
how does a translator choose words? <br> why do they choose a instead of b? c instead of a? <br>
not all translations are perfect so then, why not mix them up?
## introduction
<b>the impossible process of grasping the other side </b> is a project by Emma Prato, as part of the Special Issue 16 //Learning How to Walk While Catwalking// carried out by Piet Zwart Institute (XPUB) students on the topic of Vernacular Language Processing.
the impossible process basically wants to play around with the structure of poetry translations and mix together different versions of the same text.
translating a poem into another language is an impossible process - so what if we gather translations from different people? from different languages? and what if then we mix them up? what if we change them? what if we play with words? it probably won't solve the impossible process but for sure is an interesting experiment.
<i>//multiple translations can give us a much better sense of the poem than a single translation can, so that even if we cant read the poem in the original language, we can come closer to that experience//</i>
<b>the impossible process</b> is made of two sections: one (<b>THE CROWD</b>) is to gather //vernacular// information and the other (<b>THE MASHUP</b>) is to process texts, which can be both taken from the vernacular content or from already existing translations. The poetry archive (work in progress) will showcase the results.
## the impossible process creates processed vernacular translations. what does it mean?
//vernacular// means made by humans for humans, vernacular is the material I am gathering through the Annotation Compass°, a place where everyone can have access to, where everyone can give their contribution, regardless their background or knowledge etc.°
//processed// means that the content gathered through humans' actions is transformed by the machine to create something new.
what we are gathering, what we are processing, everything is //language//. in the case of the impossible process, we are using translations, in particular translations of poems.
## the impossible process THE MASHUP // USE THE MASHUP() FUNCTION
the impossible process THE MASHUP tries to approach the impossible process of translation by the use of a Python function that:
1. takes into account two different translations of a poem
2. finds the common words and uses them as the fixed text for the new piece of text
3. puts the results together into a new piece, randomly choosing the differences in the two texts
The resulting text will show how many different ways there are to translate a text, a line, a concept, a single word.
the impossible process is a funny way to highlight the thousand facets of a translation and create // never-ending // new texts randomly choosing the differences.
Here you will find the space to compare your own texts yourself! Just click on the button below, access the mashup() function and start to create random poems!
## the impossible process THE CROWD // CONTRIBUTE TO THE IMPOSSIBLE PROCESS
the impossible process THE CROWD consists into three annotation methods, in order to gather different material from users, then to process the texts and create new vernacular content.
The results will be part of the Archive. If you want to contribute to the Archive of vernacular translations (or experimental texts), choose a method, access the image, insert your translation and wait for your text to be used.
.one to one translation starts from a poem in x language to another x language (for example from Italian to English)
.many to one translation starts from different language translations of the same original poem. Users can choose the language they prefer to start from and then insert the translation to english (//common language) through the Annotation Compass.
.free fun translation starts from a common language and lets users interact as they want, without restrictions.
## the impossible process ARCHIVE OF VERNACULAR POEMS
the purpose of this archive is divalent: the first is to gather vernacular translations from users around the world, and to fill this archive with translations that don't exist yet -- the second is to create always new random versions of already existing translations, or of vernacular translations, to play around with language and words.

@ -0,0 +1,144 @@
---
title: Rejection Map — A Situated Topography of Rejection
author: Jian
colophon: Jian
description: How does a translator choose words? First description of the project
functions: individual_map, vernacular_map, area_map, target_map, highlight_map, ghost_map, html_tag
template: rejection_map (6 interactive forms need to be implemented, for placeholders scroll down)
---
## Rejection Map
## A Situated Topography of Rejection
A city is a shared space for individuals with diverse backgrounds, identities and ways to perceive the world around them. Probably all of them have something in common: at some point they experienced a moment of rejection. These moments of rejection can take place in various contexts. People experience rejection in private relationships or in society; in the context of work or school; in relation to their body or appearance, their gender, identity or orientation; referring to their language or nationality; when it comes to housing or navigating in the city or even the supermarket …
Rejection can also be a very personal and much lighter experience: the moment you finally let go of a bad thought; the moment you make an independent choice that is free from other's opinions; the moment you skip the party and have an evening for yourself.
In any case, rejection resonates an intimate level; a situated, embodied experience that stays with you. Many people will be able to remember those moments and even locate them on a map. And more often than not a moment of rejection shapes the individual but also the way they perceive the landscape and their surroundings.
The multiplicity of subjective, embodied and situated experiences of rejection is a way to acknowledge and embrace different ways of knowing and feeling. All these singularities dont stand isolated or alone. They are memories or anecdotes, traces or echoes of rejection. They relate, interact and overlap. Organically and ever-changing they form new maps of the city and being together on these new maps, sharing these incommensurable experiences maybe offers a sense of community and a chance for healing.
Maps are meaningful. As companions in everyday life or while travelling to unknown places they help us to orientate and navigate the world. And at the same time maps themselves represent some and reject others. Maps have the power to shape the world and to produce realities. For this collaborative and collective mapping project, people are asked to situate and describe their experiences of rejection in Rotterdam. On a map of the city they can select an area and use their own words to express their perceptions on.
Language, used to record and share human experiences, can be very powerful. We grow up using language as a way to communicate with other human beings and our own way of phrasing things is in many cases deeply connected to our identity. Especially written language adds a layer of consciousness to this: A moment of listening to yourself, before thoughtfully choosing words that best express a very intimate thought or feeling.
In what way do textual annotations redefine the notion of a map and how we relate to it?
In this alternative approach to mapping, the different perspectives on space are not only part of a research and data collecting process, but they actually create maps of their own through these linguistic annotations of affective relations. The Annotation Compass is a tool that helps to gather these particular knowledges, that altogether reveal both the commonalities and the differences of this juxtaposition. This collective approach does not necessarily aim to create a counter-map. However, it can be seen as an unfamiliar gesture that brings up questions about how, by whom and for whom maps are made. The lines between mapping and being mapped become fluid.
## How to become part of the Rejection Map
![Map of Rotterdam © OpenStreetMap contributors](/soupboat/si16-app/static/img/rejection_map/rejection_map.jpg))
[Annotation Compass Rejection-Map of Rotterdam](https://hub.xpub.nl/soupboat/generic-labels/annotate/rejection_map.jpg/)
You are invited to share your experiences with rejection in the city of Rotterdam. Please be aware that your descriptions will be visible for others to read. Your contribution is voluntary and anonymous: you decide what moments and details you want to share and there is no data collected that traces back to you. As rejection is a sensitive topic, you should only share intimate things if you feel comfortable and safe. When sharing moments that involve other individuals, please be mindful that they might not want to be exposed.
1. Open the link to the Annotation Compass. You can see the map of Rotterdam
2. Look at the map and try to think of a moment of rejection you experienced in a certain area in Rotterdam
3. Use your cursor to select this specific area of the map where you can insert your first experience. It can be a quite small area (if you want to select a building) or a bigger one (if your experience includes a whole neighborhood).
4. Please choose your own way to describe your moment of rejection. It can be short or long; formal or informal; personal or distanced; poetic or pragmatic; cryptic or explicit; anecdotal or out of context; using vernacular or academic language, slang or mother tongue; using whole sentences, single words, single letters or punctuation only … Do whatever you feel most comfortable with.
5. Click ”insert“ to save your first description or ”x“ if you want to delete it.
6. Follow the same steps to add more moments of rejection if you like. If several moments relate to the same space or area, they can overlap.
Thank you for sharing!
## Individual Maps
By adding annotations to the Rejection Map, each individual creates an Individual Map of Rejection: It displays all moments of rejection that were shared by one person. Each time someone decides to join the project, a new Individual Map is created. These maps are diverse in their appearance. Some share several experiences with rejection, others only one. Some focus on one area, while others show annotations all over the city. Especially the personal experience with rejection and the way it is described are diverse and vary from person to person. The Individual Maps validate these intimate moments that were shared.
Looking through all the Individual Maps almost feels like reading a collective diary, where the reader is encouraged to change perspectives and to emphasize with each individual.
====== interactive form ======
## Vernacular Map
The Vernacular Map combines all Individual Maps into one collective map by layering moments of rejection shared by individuals in the city of Rotterdam. While each of them is unique, they overlap and connect and whenever an Individual Map is added, the Vernacular Map changes as well. It illustrates that in our individualities we still share experiences and also space.
====== interactive form ======
## Area Map
The Area Map offers a different way to read and navigate the Rejection Map by focusing on a specific area of Rotterdam. The parameters can be adjusted to look at all annotations in a certain neighborhood or any area area of interest.
====== interactive form ======
## Target Map
The Target Map offers a different way to read and navigate the Rejection Map by filtering for one or more specific target words. The parameters can be adjusted to look at all annotations that include these targets.
====== interactive form ======
## Highlight Map
The Highlight Map offers a different way to read and navigate the Rejection Map by filtering for one specific target word and highlighting all annotations that include this target. The parameters can be adjusted.
====== interactive form ======
## Ghost Map
Sometimes descriptions of rejection can be very explicit, hurtful or triggering. The Ghost Map replaces all characters in the annotations with a specific glyph. The moments of rejection become abstract: The Ghost Map shows remnants, traces or echoes of individual experiences that are not always present, but imprinted in the memories of the individual and the city.
====== interactive form ======
## Archive
[Collective Rejection Glossary](https://pad.xpub.nl/p/Rejection_Glossary/)
![Experiment 01](/soupboat/si16-app/static/img/rejection_map/vernacular-map-experiments-01.jpg)
![Experiment 02](/soupboat/si16-app/static/img/rejection_map/vernacular-map-experiments-02.jpg)
![Experiment 03](/soupboat/si16-app/static/img/rejection_map/vernacular-map-experiments-03.jpg)
![Experiment 04](/soupboat/si16-app/static/img/rejection_map/vernacular-map-experiments-04.jpg)
![Experiment 05](/soupboat/si16-app/static/img/rejection_map/vernacular-map-experiments-05.jpg)
![Experiment 06](/soupboat/si16-app/static/img/rejection_map/vernacular-map-experiments-06.jpg)
![Experiment 07](/soupboat/si16-app/static/img/rejection_map/vernacular-map-experiments-07.jpg)
![Experiment 08](/soupboat/si16-app/static/img/rejection_map/vernacular-map-experiments-08.jpg)
![Experiment 09](/soupboat/si16-app/static/img/rejection_map/vernacular-map-experiments-09.jpg)
![Experiment 10](/soupboat/si16-app/static/img/rejection_map/vernacular-map-experiments-10.jpg)
![Experiment 11](/soupboat/si16-app/static/img/rejection_map/vernacular-map-experiments-11.jpg)
![Experiment 12](/soupboat/si16-app/static/img/rejection_map/vernacular-map-experiments-12.jpg)
![Experiment 13](/soupboat/si16-app/static/img/rejection_map/vernacular-map-experiments-13.jpg)
![Experiment 14](/soupboat/si16-app/static/img/rejection_map/vernacular-map-experiments-14.jpg)
![Experiment 15](/soupboat/si16-app/static/img/rejection_map/vernacular-map-experiments-15.jpg)
![Experiment 16](/soupboat/si16-app/static/img/rejection_map/vernacular-map-experiments-16.jpg)
![Experiment 17](/soupboat/si16-app/static/img/rejection_map/vernacular-map-experiments-17.jpg)
![Experiment 18](/soupboat/si16-app/static/img/rejection_map/vernacular-map-experiments-18.jpg)
![Experiment 19](/soupboat/si16-app/static/img/rejection_map/vernacular-map-experiments-19.jpg)
![Experiment 20](/soupboat/si16-app/static/img/rejection_map/vernacular-map-experiments-20.jpg)
![Experiment 21](/soupboat/si16-app/static/img/rejection_map/vernacular-map-experiments-21.jpg)

@ -0,0 +1,103 @@
---
title: Rotterdam Impressions
author: Ål Nik (Alexandra Nikolova)
colophon: A showcase of the 'Annotation Compass'
description: A collective map of Rotterdam impressions, annotated by the first year Experimental Publishing students
functions: Adjectives, Nouns filers, Most common words filter
---
## Introduction
### rotterdam impressions with the 'annotation compass'
We all arrived here from different places, backgrounds, histories and languages. As a group, we are building our relationships and exploring our personalities. Brought to this new place, we discover it both individually and collectively. The city is our everyday environment - we navigate in it to go to school, to go home, to do our grocery, to have a drink out. We spend some of these moments alone, sometimes with someone else. In a short moment of reflection, we all remembered our first or strongest impressions of some particular areas of Rotterdam, which triggered a memorable emotion in us.
These illustrated subjective maps represent the impressions of the first year master students of Experimental Publishing at the Piet Zwart Institute. Their situated experiences trigger various ways to describe their new home: Rotterdam. They were invited to reflect on the way they remember some of the areas in the city and to describe how they feel about them. In words, in their own vernacular English - the common language that is not native to anyone of them but the main language they communicate between each other. Afterwards, their descriptions were filtered by a Python function to extract the most common words they used whilst describing their perceptions of the selected areas on the map, divided in three main categories - adjectives, nouns and pronouns. The final outcome is this short list of the most common impressions of various places and illustrated maps as an artistic expression of the author.
### the most common...
In the following lists we observe the most common words the annotators used in their descriptions. They were asked to describe how they feel about areas and places they see on the map which trigger some memories and emotions in them. By using the Python filters for extracting the words that are adjectives (the words people used to describe their impressions of Rotterdam and various areas and places in it), nouns (the objects that we see people referring to) and pronouns (they way the approached the reflection) in combination with a filter for the most common words (which shows us which words are used the most and how many times), we can observe what are the feelings and impressions that they share as a new collective. What are the places and emotions that are common for some of them?
```python
[('I', 53), ('my', 21), ('we', 19), ('me', 16), ('i', 13), ('place', 9), ('super', 9), ('We', 9), ('happy', 8), ('excited', 8), ('our', 8), ('were', 8), ('beautiful', 7), ('Rotterdam', 7), ('first', 6), ('area', 6), ('calm', 5), ('nervous', 5), ('big', 5), ('fig', 5), ('tree', 5), ('safe', 4), ('home', 4), ('inspired', 4), ('city', 4), ('little', 4), ('room', 4), ('shocked', 4), ('fun', 3), ('frustrated', 3), ('friendly', 3), ('good', 3), ('best', 3), ('bridge', 3), ('school', 3), ('nice', 3), ('station', 3), ('weird', 3), ('sad', 3), ('bike', 3), ('neighborhood', 3)]
```
![Screenshot of the result](/soupboat/si16-app/static/img/rotterdam_impressions/map_00.jpg)
### vernacular map
The visual maps of [rotterdam impressions] depict how many times specific words were mentioned in the descriptions overall - the positions of the words are not related to the specific areas where they were mentioned because they are a collective print of how many times these words were used anywhere on the map. By printing each time a word was used, we can see the intensity of words used by the annotators. They are coloured due to their category - adjectives are in green, nouns are in orange and pronouns - in pink.
![Screenshot of the result](/soupboat/si16-app/static/img/rotterdam_impressions/map_01.jpg)
### how are we describing
"I" and "we" are the most common pronouns (in pink) that were used in the annotations. You can see that "I" was used as twice more often that "we", but perhaps because of the context of us describing our first impressions of a new place where everyone arrived to study and work together as a group, some of the experiences were still connected to a collective moment.
![Screenshot of the result](/soupboat/si16-app/static/img/rotterdam_impressions/map_03.jpg)
### adjectives as emotions and impressions; nouns as objects
In the context of the annotation compass, the adjectives (in green) were filtered and extracted as impressions and descriptions of the new place where people arrived. They are either describing Rotterdam and places inside it, or the way the annotator feels about them. Either way, in most of the cases we can still get the emotion that lays behind the word used. The most common nouns are understood as the objects (in orange) - the ones people are relating most often to.
![Screenshot of the result](/soupboat/si16-app/static/img/rotterdam_impressions/map_04.jpg)
![Screenshot of the result](/soupboat/si16-app/static/img/rotterdam_impressions/map_02.jpg)
## Functions
To filter the adjectives from the annotations:
```
def adjectives(text):
adj = []
for (word, tag) in labels_blob.tags:
if tag == 'JJ':
adj = adj + [word]
return adj
```
To filter the nouns from the annotations:
```
def nouns(text):
noun = []
for (word, tag) in labels_blob.tags:
if tag == 'NN':
noun = noun + [word]
return noun
```
To filter the most common words from the annotations:
```
def most_common(words):
freq_dist_pos = FreqDist(words)
return freq_dist_pos.most_common(100)
```
## Process
Getting to the final showcase of the "annotation compass" was a long way of going back and forth whilst learning my first steps in Python. From the beginning my idea was to create vernacular map of our rotterdam impressions using our words in describing emotions and experineces. Here is what happened on the road:
### 0. background story
In May 2021 I went through a massive burnout which resulted in me going to the hospital and not being able to climb the stairs at home normally for a week, because I couldn't take a deep breath. This traumatic experience got me started paying more attention to the signs of me getting overwhelmed; and also since then I have been living in fear of going through a similar experience again.
After that crazy time, I had to organise my moving to Rotterdam and do some work on the way around Europe. After a year in isolation because of the pandemic, this was a dramatic change and I was afraid I might have another panic attack whilst being alone in the train in the middle of nowhere. In order to keep a track of my emotions and the way I feel, I started mapping them. That experiment made me think more of how to use our "annotation compass" to map our group's experiences and feelings.
![Screenshot of the result](/soupboat/si16-app/static/img/rotterdam_impressions/prototype_00.jpg)
### 1. ideas & prototypes
The initial idea was to try mapping our emotions when reflecting on our first experiences in Rotterdam. In order to visualise that, I made a few prototypes by me mapping the places I feel most strongly about and then doing the same with my colleague Supisara.
The prototypes were adjectives put on the map of Rotterdam, describing how we felt about a specific place or area. Later on, I figured it would be much more interesting to give freedom to the annotator to choose how they want to describe their perceptions. And after that, I could filter their texts in order to extract the adjectives.
![Screenshot of the result](/soupboat/si16-app/static/img/rotterdam_impressions/prototype_01.jpg)
### 2. collecting annotations
We used the "annotation compass" to write down some reflections about our arrival in Rotterdam and the way we feel about some places and areas. The invitation to the group was to take a few minutes to reflect on their arrival in the city and they feel strongly on some areas and spaces; and to describe the way they feel about them.
![Screenshot of the result](/soupboat/si16-app/static/img/rotterdam_impressions/prototype_02.jpg)
### 3. filtering the annotations with python
Since there was no restriction what words the use in the descriptions, I applied a combination of Python functions in order to process the data and extract the most common words. I also looked up for how many times the most common adjectives, nouns and pronouns were used. Looking at the result of the text processing, I created illustrated maps with the number of times these most common words were used.

@ -0,0 +1,38 @@
---
title: "Etc Portal to Contamination"
description: "The Etc Portal to Contamination is a research into the negative space one can find in texts."
colophon: "Chae, Gersande"
url: "https://hub.xpub.nl/soupboat/~chae/api/"
---
<!-- the contents above are the metadata and the about. this is an example. we can add more parameters if we need them (like a cover image, a link to the project, etc) -->
<!-- ideally there is also a link to the project page in which the real things happen -->
<!-- now this link to the prototype, but later it will link to the right endpoint in the si16 api -->
<!-- the contents below are the showcase -->
The Etc Portal to Contamination is a project initiated by Chaeyoung Kim and Gersande Schellinx in the context of the Special Issue 16 (2021), around the topic of Vernacular Language Processing. It is a subgroup project hosted by the overall project: Learning How To Walk While Catwalking.
The Etc Portal to Contamination is a research into the negative space one can find in texts. Hosted by the means of simple keywords, expressions and punctuation marks… These implicit markers ('et cetera', ' etc ', ' etc.', 'and-so-on', 'and-so-forth', 'and others', 'et al.', 'and all the rest', 'and on and on', 'along with others, blablabla, blabla, and much more, '...', '(...)','[...]', '[. …]'), often leave out some information for different reasons: may that be due to a lack of time, because the author assumes you understand what they imply, or don't feel the need to make a further listing of certain items.
More information about the Etc Project to Contamination(https://pzwiki.wdka.nl/mw-mediadesign/images/1/1b/Graph_etcfunction_update.jpg)
## etc.
C and G's etcetera list combines different branches of their conceptual understanding of what the etc is.
etcetera = ['et cetera', ' etc ', ' etc.', 'and-so-on', 'and-so-forth', 'and others', 'et al.', 'and all the rest', 'and on and on', 'along with others, blablabla, blabla, and much more, '...', '(...)','[...]', '[. ...]']
## Contamination
If the contamination word might come across as a negative notion, C & G mean it as a neutral term. What is designated here as "contamination" is the pure agency of introducing contaminants, foreign entities to a body of text and make it become part of it, in a seamless way.
## Co-authorship
The text output returned to the user, is the user's as much as it is C and G's. The last line "The Neverending Backstage Story is an original text by Gersande | Chaeyoung | ..." invites the users to add their pen name after contamination of the text.
This feature of the Etc Portal To Contamination is one essential aspect of C & G's project. As within the idea of vernacular language processing, they aimed to question the established authority in publishing, hierarchy in textual platforms, alleged legitimacy in speech and narrow consideration in the legitimate world of references.
## Colofon
This project has been made possible thanks to Manetta Berends, Michael Murtaugh, Clara Balaguer, Steve Rushton, Cristina Cochior and all our dear XPUB classmates.
## Expected outcomes
By submitting their textual information, thoughts, questions, or else, the users have submitted what they consider legitimate content, that will then merge with the "original" text and become one. As such, the text itself is ongoing. As long as users contaminate it, the text will grow, change. Subvert, invert narrative.

@ -0,0 +1,38 @@
---
title: "Etc Portal to Contamination"
description: "The Etc Portal to Contamination is a research into the negative space one can find in texts."
colophon: "Chae, Gersande"
url: "https://hub.xpub.nl/soupboat/~chae/api/"
---
<!-- the contents above are the metadata and the about. this is an example. we can add more parameters if we need them (like a cover image, a link to the project, etc) -->
<!-- ideally there is also a link to the project page in which the real things happen -->
<!-- now this link to the prototype, but later it will link to the right endpoint in the si16 api -->
<!-- the contents below are the showcase -->
The Etc Portal to Contamination is a project initiated by Chaeyoung Kim and Gersande Schellinx in the context of the Special Issue 16 (2021), around the topic of Vernacular Language Processing. It is a subgroup project hosted by the overall project: Learning How To Walk While Catwalking.
The Etc Portal to Contamination is a research into the negative space one can find in texts. Hosted by the means of simple keywords, expressions and punctuation marks… These implicit markers ('et cetera', ' etc ', ' etc.', 'and-so-on', 'and-so-forth', 'and others', 'et al.', 'and all the rest', 'and on and on', 'along with others, blablabla, blabla, and much more, '...', '(...)','[...]', '[. …]'), often leave out some information for different reasons: may that be due to a lack of time, because the author assumes you understand what they imply, or don't feel the need to make a further listing of certain items.
More information about the Etc Project to Contamination(https://pzwiki.wdka.nl/mw-mediadesign/images/1/1b/Graph_etcfunction_update.jpg)
## etc.
C and G's etcetera list combines different branches of their conceptual understanding of what the etc is.
etcetera = ['et cetera', ' etc ', ' etc.', 'and-so-on', 'and-so-forth', 'and others', 'et al.', 'and all the rest', 'and on and on', 'along with others, blablabla, blabla, and much more, '...', '(...)','[...]', '[. ...]']
## Contamination
If the contamination word might come across as a negative notion, C & G mean it as a neutral term. What is designated here as "contamination" is the pure agency of introducing contaminants, foreign entities to a body of text and make it become part of it, in a seamless way.
## Co-authorship
The text output returned to the user, is the user's as much as it is C and G's. The last line "The Neverending Backstage Story is an original text by Gersande | Chaeyoung | ..." invites the users to add their pen name after contamination of the text.
This feature of the Etc Portal To Contamination is one essential aspect of C & G's project. As within the idea of vernacular language processing, they aimed to question the established authority in publishing, hierarchy in textual platforms, alleged legitimacy in speech and narrow consideration in the legitimate world of references.
## Colofon
This project has been made possible thanks to Manetta Berends, Michael Murtaugh, Clara Balaguer, Steve Rushton, Cristina Cochior and all our dear XPUB classmates.
## Expected outcomes
By submitting their textual information, thoughts, questions, or else, the users have submitted what they consider legitimate content, that will then merge with the "original" text and become one. As such, the text itself is ongoing. As long as users contaminate it, the text will grow, change. Subvert, invert narrative.

@ -0,0 +1,569 @@
# IMPORT
# to work with files in folder
import os
# to work with json files
import json
from urllib.request import urlopen
# to create the Flask app
from flask import Flask, render_template, request, url_for, redirect, jsonify, abort
# to import text contents and metadata from markdown files
from flaskext.markdown import Markdown
import frontmatter
# to cast string arguments into the required types
from pydoc import locate
# to work with notebooks
#
# to import notebook files
import nbimporter
nbimporter.options['only_defs'] = False
import importlib
# to read and execute the content of notebooks
import nbformat
from nbconvert import HTMLExporter, MarkdownExporter
from nbconvert.preprocessors import ExecutePreprocessor
# not sure about this is in the nbconvert documentation
from traitlets.config import Config
# to work with placeholder descriptions
from random import choice
# Subgroup Projects import
# not really happy of this routing for the module but
ac = importlib.import_module("projects.annotation-compass.annotation_compass")
# FUNCTIONS
def filenames(folder, remove_ext = False):
''' Read all the functions in a folder '''
names = []
for entry in os.scandir(folder):
# add to the list only proper files
if entry.is_file(follow_symlinks=False):
# remove the extension from the filename
n = os.path.splitext(entry.name)[0]
if remove_ext:
n = entry.name
names.append(n)
return names
def dirnames(folder):
''' Return all the folders in a folder '''
names = []
for entry in os.scandir(folder):
# add to the list only proper files
if not entry.name.startswith('.') and entry.is_dir():
# remove the extension from the filename
names.append(entry.name)
return names
# not really sure about this file -> module -> function thing!
# could someone help pls ?
def get_function(name, folder):
''' Dynamic import a function from a folder '''
file = __import__(f'{folder}.{name}')
module = getattr(file, name)
function = getattr(module, name)
return function
def get_function_info(function):
''' Extract info from a function '''
name = function.__name__
description = function.__doc__
parameters = []
output = ''
# TODO: default values
# populate a list of tuple with patameter, type
for param in function.__annotations__.keys():
if param == 'return':
output = function.__annotations__[param].__name__
if param != 'return':
parameters.append((param, function.__annotations__[param].__name__))
return(name, description, parameters, output)
def print_info(function):
''' Print the info of a function nicely '''
name, description, parameters, output = get_function_info(function)
# very important feature
from kaomoji.kaomoji import Kaomoji
kao = Kaomoji()
header = f'----------{kao.create()}'
footer = '-' * len(header)
print(header)
print(name)
print(description)
print('Input:')
for param, tp in parameters:
print(f' {param}, of type {tp}')
print(f'Returns a {output}')
print(footer)
def generate_function_list():
''' Build a list of the function '''
functions = []
for function in filenames("./notebooks"):
try:
fx = get_function(function, notebooks)
name, description, parameters, output = get_function_info(fx)
# print(f'{function} function is ok')
# print(f'Description is: {description}')
if description == None:
description = placeholder_description('function')
f = {
"title": name,
"description": description
}
except:
# print('--->error so placeholder fx')
f = {
"title": function,
"description": placeholder_description('function')
}
functions.append(f)
# print(functions)
with open('./static/functions.json', 'w') as file:
json.dump(functions, file)
def placeholder_description(subject):
''' Return a placeholder description of a subject '''
# ok this is a meme but is to make the notebooks a bit more waterproof
adjs = ['useful', 'nice', 'intresting', 'incredible', 'wow']
placeholders = [f'A super', f'A {choice(adjs)} and', f'You wont believe to this', f'Yet another' ]
return f'{choice(placeholders)} {choice(adjs)} {subject}'
# optionally you can pass a boolean argument to execute the notebook before the export
# but it is a slow process so by default is not active
# TODO: markdown export instead of HTML ?
# TODO: extract images from base64
def get_notebook_contents(filename, execute = False):
''' Export notebook contents as HTML. '''
with open(filename) as f:
nb = nbformat.read(f, as_version=4)
if execute:
ep = ExecutePreprocessor(timeout=600, kernel_name='python3')
ep.preprocess(nb, {'metadata':{'path':'notebooks/'}})
html_exporter = HTMLExporter()
html_exporter.template_name = 'basic'
(body, resources) = html_exporter.from_notebook_node(nb)
return body
# EXPORT AS MARKDOWN
def get_notebook_md(filename, execute = False):
''' Export notebook contents as Markdown. '''
with open(filename) as f:
nb = nbformat.read(f, as_version=4)
if execute:
ep = ExecutePreprocessor(timeout=600, kernel_name='python3')
ep.preprocess(nb, {'metadata':{'path':'notebooks/'}})
md_exporter = MarkdownExporter()
(body, resources) = md_exporter.from_notebook_node(nb)
return body
def get_contents(filename, directory = './contents'):
''' Return contents from a filename as frontmatter handler '''
with open(f"{directory}/{filename}", "r") as f:
content = frontmatter.load(f)
return content
def get_meta(filename, directory = './contents'):
''' Return contents from a filename as frontmatter handler '''
with open(f"{directory}/{filename}", "r") as f:
metadata, content = frontmatter.parse(f.read())
return metadata
# EVENT MODE
def is_event_mode():
return False
# FLASK APP
base_url = "si16"
notebooks = "notebooks"
projects = "projects"
# create flask application
app = Flask(__name__,
static_url_path=f'/soupboat/{base_url}/static',
static_folder=f'/soupboat/{base_url}/static')
Markdown(app, extensions=['extra'])
# add the base_url variable to all the flask templates
@app.context_processor
def set_base_url():
return dict(base_url = base_url)
# Generate functions list
generate_function_list()
# For specific pages we can build and link dedicated templates
# Homepage
@app.route(f"/{base_url}/")
def home_page():
# get the basic info of the website from the /contents/si16_info.md file
info = get_contents("si16_info.md").to_dict()
projects_list = []
for project in dirnames("./projects"):
page = get_contents("documentation.md", f"./{projects}/{project}").to_dict()
page['slug'] = project
projects_list.append(page)
# get the list of the projects, the functions, and the corpora
home = {
**info,
"projects": projects_list,
"functions": filenames("./notebooks"),
"corpora": dirnames("./static/corpora")
}
return render_template("home.html", **home)
# # About Page
# @app.route(f"/{base_url}/about/")
# def about_page():
# about = get_contents('about.md')
# return render_template("about.html", title = about['title'], description = about['description'], contents=about.content)
# For generic pages we can include a common template and change only the contents
@app.route(f"/{base_url}/<slug>/")
def dynamic_page(slug = None):
try:
page = get_contents(f"{slug}.md").to_dict()
# page is a dictionary that contains:
# - all the attributes in the markdown file (ex: title, description, soup, etc)
# - the content of the md in the property 'content'
# in this way we can access those frontmatter attributes in jinja simply using the variables title, description, soup, etc
return render_template("page.html", **page)
except FileNotFoundError:
# TODO: a proper not found page
return render_template('404.html')
# List of projects
@app.route(f"/{base_url}/projects/")
def projects_list():
# get a list of the functions from the notebooks folder
projects_list = []
for project in dirnames("./projects"):
page = get_contents("documentation.md", f"./{projects}/{project}").to_dict()
page['slug'] = project
projects_list.append(page)
info = get_contents("projects.md").to_dict()
# generate a link to each function
return render_template("projects.html", projects=projects_list, **info)
# Single project
@app.route(f"/{base_url}/projects/<project>/")
def p_info(project = None):
try:
page = get_contents("documentation.md", f"./{projects}/{project}").to_dict()
if 'showcases' in dirnames(f"./{projects}/{project}"):
page['showcases'] = []
showcases_list = filenames(f"./{projects}/{project}/showcases/")
for showcase in showcases_list:
print(showcase)
info = get_meta(showcase + '.md', f"./{projects}/{project}/showcases/")
info['slug'] = showcase
page['showcases'].append(info)
return render_template("project.html", **page)
except FileNotFoundError:
return render_template('404.html')
@app.route(f"/{base_url}/projects/<project>/<showcase>/")
def p_showcase(project = None, showcase = None):
print(project)
print(showcase)
try:
page = get_contents(f"{showcase}.md", f"./{projects}/{project}/showcases").to_dict()
return render_template('page.html', **page)
except FileNotFoundError:
return render_template('404.html')
return render_template('404.html')
# List of functions
@app.route(f"/{base_url}/functions/")
def functions_list():
info = get_contents("functions.md").to_dict()
# get a list of the functions from the notebooks folder
functions = filenames("./notebooks")
with open('./static/functions.json', 'r') as file:
functions = json.load(file)
# generate a link to each function
return render_template("functions.html", functions=functions, **info)
# Single Function page
@app.route(f"/{base_url}/functions/<function>/")
def f_info(function=None):
if function in filenames(f"./{notebooks}"):
fx = get_function(function, notebooks)
name, description, parameters, output = get_function_info(fx)
# executing a notebook takes a lot of time mmm should we just send them without the results of the examples? or save it executed?
# documentation = get_notebook_contents(f"./{notebooks}/{function}.ipynb")
documentation = get_notebook_md(f"./{notebooks}/{function}.ipynb")
return render_template("function.html", title=name, description=description, parameters=parameters, output=output, documentation=documentation)
# TODO: meaningful error code return
else:
return render_template('404.html')
# Function API page
@app.route(f"/{base_url}/api/<function>/")
def f_api(function=None):
if function in filenames(f"./{notebooks}"):
fx = get_function(function, notebooks)
name, description, parameters, output = get_function_info(fx)
query_params = []
for param, tp in parameters:
a = request.args.get(param)
# cast the type of the argument to the type that the function requires
if tp == "list":
a = request.args.getlist(param, type=str)
else:
tp = locate(tp)
a = tp(a)
# print(a)
query_params.append(a)
return fx(*query_params)
# TODO: meaningful error code return
return "mmmm there is no function with this name sorry"
# TODO: maybe the event mode could be a decorator ? to avoid the request from the client every time and instead just do it in the server ?
# EVENT MODE
import mimetypes
@app.route(f"/{base_url}/api/is-event-mode/")
def event_mode():
if is_event_mode():
event_path = '/static/event'
event = dirnames("./static/event")
page = request.values.get('page', '')
snippets = []
if page in event:
snippets = []
for entry in os.scandir(f".{event_path}/{page}"):
# add to the list only proper files
if entry.is_file(follow_symlinks=False):
snippet = {
"name": entry.name
}
ty, enc = mimetypes.guess_type(entry.name)
if 'text' in ty:
snippet['type'] = 'text'
with open(f".{event_path}/{page}/{entry.name}", 'r') as f:
content = f.read()
snippet['content'] = content
elif 'image' in ty:
snippet['type'] = 'image'
snippet['content'] = f"{event_path}/{page}/{entry.name}"
elif 'video' in ty:
snippet['type'] = 'video'
snippet['content'] = f"{event_path}/{page}/{entry.name}"
elif 'audio' in ty:
snippet['type'] = 'audio'
snippet['content'] = f"{event_path}/{page}/{entry.name}"
snippets.append(snippet)
return {"event": True, "snippets": snippets}
return {"event": True}
return {"event": False}
# PROJECTS API pages
# custom section ===========
# warning mothership reporting
# we will add here the custom endpoints for each subgroup project
# 2MB max for file upload (we can change this)
app.config['MAX_CONTENT_LENGTH'] = 2 * 1000 * 1000
@app.route(f"/{base_url}/annotation-compass/", methods=['GET','POST'])
def annotation_compass():
if request.method == 'POST':
# Upload new file
ac.upload_file(request)
images = ac.list_images()
return render_template('annotation-compass.html', title='Annotation Compass', images=images)
@app.route(f"/{base_url}/annotation-compass/annotate/<image>/")
def annotate_image(image=None):
print(app.static_url_path, app.static_folder)
description = ''
try:
description = ac.get_image_description(image)
except:
print('there is no description')
return render_template('annotate_image.html', image=image, description=description)
@app.route(f"/{base_url}/annotation-compass/add-label/", methods=['GET','POST'])
def insert_label():
if request.method == 'POST':
return ac.insert_label(request)
@app.route(f"/{base_url}/annotation-compass/get-labels/<image>/")
def get_labels_list(image=None):
return ac.get_labels_list(image)
# JIAN API
def generate_urls(elements):
a_list=''
for element in elements:
url=f'https://hub.xpub.nl/soupboat/individual-maps/{element}/'
a = f'<a href="{url}">{element}</a><br />'
a_list = a_list + a
return a_list
@app.route(f"/{base_url}/api/label/rejection/individual_map/")
def individual_map():
image = 'xpub1_rotterdam_map_1400px.jpg'
url = f"https://hub.xpub.nl/soupboat/generic-labels/get-labels/?image={image}"
response = urlopen(url)
data_json = json.loads(response.read())
all_userIDs = []
for label in data_json['labels']:
all_userIDs.append(label['userID'])
set_userIDs = set(all_userIDs)
im = get_function('individual_map', notebooks)
individual_labels = im(data_json['labels'], ['5058763759','5941298752'])
to_html = get_function('html_tag', notebooks)
result = to_html(individual_labels, True, True, True, True)
return result
@app.route(f"/{base_url}/projects/map/rejection_map/")
def rejection_map():
return render_template('rejection_map.html')
# ===============
# Error handlers!
@app.errorhandler(400)
def error_400(e):
# bad request or invalid url
return render_template('400.html'), 400
@app.errorhandler(403)
def error_403(e):
# forbidden (for invalid key) for evemt mode when it's not the 17th of the month
return render_template('403.html'), 403
@app.errorhandler(404)
def error_404(e):
# page not found
return render_template('404.html'), 404
@app.errorhandler(500)
def error_500(e):
# internal server error
return render_template('500.html'), 500
@app.errorhandler(502)
def error_502(e):
# bad gateaway
return render_template('502.html'), 502
@app.errorhandler(503)
def error_503(e):
# service temporarily unavailable, for secret breaks
return render_template('503.html'), 503
@app.errorhandler(504)
def error_503(e):
# ???????? gateway timeout shall we put it?????
return render_template('504.html'), 504
# RUN
app.run(port="3131")

@ -0,0 +1 @@
{"functions": [{"title": "vernacular_map", "description": "A nice and intresting function"}, {"title": "text_file_to_blob", "description": null}, {"title": "shout", "description": "Repeat the vowels in a string for a specified number of times"}, {"title": "reverse", "description": "Reverse the input sentence by sentence"}, {"title": "cocktail_generator", "description": "randomly chooses ingredients from a menu for a nice cocktail recipe"}, {"title": "blob_to_excerpts_list", "description": null}, {"title": "respell", "description": null}, {"title": "mashup", "description": null}, {"title": "reveal", "description": null}, {"title": "repeat", "description": "Repeat a string for a specified number of times"}, {"title": "highlight_map", "description": "A nice and intresting function"}, {"title": "individual_map", "description": "You wont believe to this function"}, {"title": "bridge", "description": "You wont believe to this function"}, {"title": "add_target_info", "description": null}, {"title": "area_map", "description": "A super useful function"}, {"title": "stich", "description": null}, {"title": "input-back-to-text", "description": "You wont believe to this function"}, {"title": "target_map", "description": "A super useful function"}, {"title": "ghost_map", "description": "A nice and intresting function"}, {"title": "html_tag", "description": "A super useful function"}]}

@ -0,0 +1,13 @@
etcetera = ['et cetera', ' etc ', ' etc.', '...', '[...]', '[. ...]', 'and-so-on', 'and-so-forth', 'and others', 'et al.', 'and all the rest', 'and on and on', 'along with others, blablabla, and much more]
Late afternoon, it has been made official by the authorities: the building has to close. No more access will be granted to anyone. The enumeration has been too fast, too sudden and has reached an unprecedented amount of individuals. The clouds froze, the grass faded down, the branches broke into pieces, the ducks scattered in pounds with no bottom, the birds fell mid-air, and much more… Or not exactly like that. But it felt as such. No one could have predicted it, yet, it happened. By lack of time, a lot of things had to be left aside. “Isolated, between two circles (…) On the news, a lot seems to be left out.” Maybe it is due to a lack of information… Or maybe they are trying to save up some for the days to come. Andrew sent us a message on our Canada group chat: “Canada releases 50 million pounds from maple syrup reserve amid global shortage”. Canada is at the heart of the worlds maple syrup supply chain and they seem to know more than the rest of us, for the supply hasnt ceased! They have been stocking up maple syrup, in case of a crisis. How amazed was I to hear: the world might never run out of maple syrup! At least not before a few more international crisis.
Some might think “is the maple syrup industry responsible?!” and others might think this nonsense is bigger than us. One might wonder: “how did it all turned to such an unfamiliar pattern so quickly?”. One does wonder, one wonders everyday. One has infiltrated books, essays, articles, comments, written shows, made documentaries, recorded podcasts, songs, etc… Is there anything left to say? How will the rest of us get a hold of it? Is it chaos or a natural occurrence? Is it oblivion, tyranny or cheer contingency?
No one ever came to the end of it, and whenever one tries, something else comes and interrupts it. We run out of time, run out of space. Curate speech, classify facts, choose references, ignore others and this goes on and on and on. You might see it on paper or even in a train station… The other day, I was getting in the train to go back to Amsterdam. At the Rotterdam Centraal station, on my way to my favourite seat (I like to sit next to the luggage racks, so there is no one in vis-à-vis to me) I overhear a conversation between a woman talking with an elderly man sitting next to her: “Do you need to pay something on this train?” “Oh yes, did you not get the toeslag?” “No, no.”. By the time I take my seat, she stands up, without a coat nor a bag, the chipkaart from the elderly man in hand and goes to the perron in order to pay the toeslag on the machine dedicated for that matter. The first one she tries is in front of my window I want to shout “you just checked yourself out! This is not the right machine!” (I did that once myself, very upsetting). She looks back at it startled, tries again. Still not working. She realises her mistake. Runs to the over side. By the time she paid the mans toeslag the trains doors close. I see her run to one door, then try another, but its too late, the train is leaving. I stand up, so surprised by what is happening in front of me, speechless. She stands there, with the chipkaart in her hand, without a bag nor a coat. And the train leaves and all the rest is unknown to my keyboard.
What made this storys interruption? The trains departure? My incapacity at imagining what could have happened else? My unwillingness at citing options, possibilities, opportunities? Did they even know each other? Or was this a good action gone bad? How do you explain such an acte manqué to anyone? Some stories have no backside. Some stories are made to be chopped off, distributed away, never fully grasped. Some stories you feel you know, you probably do, without ever having read yourself. For its just here, hanging in the air, contaminating your mind and much more without you knowing about it.
There is a cherry without a pit in my eye, a lemon with no skin in your hand, a soap-shaped potato in your bathroom, a wrinkled puppy dog on your laps, a coverless book on the street, a virgin cocktail with integrity, an alcoholic beverage with ethic, a street-lamp with grain, a low-quality screen in the spas waiting room, some butterflies with no wings, lady-bugs with nails, chairs with feet, legs with hairs, nose without nostrils, mouth with no breath, inspiration without context, structures of defamation, a bald kid with hair, a kettle with blisters, a father with a sister, a sibling with temperament, a ramp with no wheels, flowers within a vase in a vase, a sight with no red, a sight with no blue, a sight with no green, a steel of humidity, a stain of clumsiness, some oracles statue, and much more. For there is no limit to the associations one can make within language, and no boundaries as to how you, whose eyes are actively running along the page, will read those associations, and make new ones out of them. Yet, I stopped my listing in the midd… Let me guess. You like to dance? Sing? Draw a bit on the side, have many hobbies but are no good at any of them? Or at least pretend so? You are unmentioned, yet full of references, like to shine, but only when invited to do so, like to be alone, but not lonely. Highly capable and naturally lazy, creative and bored, funny and awkward, blablabla. You know what? Really, you are too preoccupied. You know what? Really, you are too full of yourself. You know what? Really, you are too tired. You know what? Really, your head is in the clouds. You know what? Really, your head is in the screens. You know what? Really, your screen is full of heads. You know what? Really, your screen is full of heads in the clouds. You know what? Really, your screen is full of tired heads in the clouds. You know what? Really, your screen is full of tired heads in the cloud that are full of themselves. You know what? You are too preoccupied…
Some thoughts have been scratching my throat lately. Its not the c-word I promise. I wake up in the morning, and as if the compilation of dreams have reached the top of my body, I burst in coughs, spit out sounds of fury, trying to take shape, say something, but I take two paracetamols, 1L of ginger tea, some thyme drops and the words are swallowed back into my stomach. Theyve tried to contaminate the outside, but I keep them inside, safe, where no one can reach them. They try to invade others, once in a while, they manage a smooth escape. Yes, sometimes I just cant shut up, the scratch reaches my lips, and spits onto one, that makes two and two brings it to four, four to eight and all the rest is history.
We dont appreciate what is within our guts, the acids that are at work, the pumping trying to stay on pace, the foreign items being assimilated in order to keep everything working. “We do not talk of what is on the other side [. …] The only things we mention are the ones who come out of us, which we suddenly do not feel anymore but can witness.” Its a strange thing we do not share what is closest to us, which we might think is the easier to describe due to proximity. On the contrary, when one does not see or smell or taste, one doesnt trust the feeling. It might be yours, but youd rather leave it out, in order to focus on what is to come. For you have no proof of your feeling until it comes out. And then you might mention it, but really you want to disregard that feeling as fast as you can, roll it aside and not mention it, just one thing amongst others, not worth mentioning. Those can be many things: there was mitvo, there was oop, eep, rhoeadiar, stip and storn, sewats, irsha, dead niks, etc.
In the area called Te-ecetra, there is a cat on a tree eating a treat, drinking a tea, he got a cart full of rats and the tracks trace back to tac-tics and tic-tacs. The cat is known in Te-ecetra, for being fond of tea and treats like English people are fond of afternoon teas and biscuits. The cat is always seen carrying a cart full of rats, that leave traces around trees. For in order to enjoy his teas and treats, the cat needs to be high in trees, so not to trigger the rats in the cart, with the sight of tip-top teas and treats. Then alone on the top of his trees, the cat can peacefully, think for hours, days sometimes weeks, how good it is to be a cat in a tree, with a cart full of rats, enjoy teas and treats, in a remote area, while others might no be able to be like the cat in a tree, with a cart full of rats, et cetera, et cetera.

@ -0,0 +1,233 @@
h3,
h2 {
margin-top: 0;
}
.description {
grid-column: 1 / span 2;
}
.function-io,
.endpoints,
.playground {
margin: 30px 0;
}
.playground,
.playground-input,
.playground-output,
.notebook {
grid-column: -1 / 1;
display: grid;
grid-template-columns: 1fr 1fr 1fr;
grid-column-gap: 30px;
}
.function-io {
grid-column: 1 / -1;
display: grid;
grid-template-columns: 1fr 1fr 1fr;
place-items: start;
grid-gap: 30px;
}
.function-io > * {
width: 100%;
height: 100%;
}
.function-io .input,
.function-io .output,
.playground .input {
background-color: white;
aspect-ratio: 1;
border-radius: 50%;
}
.function-io h3 {
margin: 0;
}
.function-io ul {
font-family: "Necto";
list-style: none;
padding: 0;
}
.endpoints {
grid-column: 1 / -1;
}
.endpoints code {
background-color: white;
margin: 30px 0;
white-space: initial;
}
.endpoints .example {
margin: 15px 0;
}
.playgroud-input .input {
grid-column: 1 / span 1;
align-self: center;
}
.playground-output .output {
grid-column: 2 / span 2;
background-color: white;
border-radius: 12px;
padding: 12px;
min-height: calc(var(--text) * 2);
}
.playground label {
display: block;
}
.playground * + label {
margin-top: 24px;
margin-bottom: 6px;
}
.playground input[type="submit"] {
margin-top: 30px;
}
/* .notebook code {
position: relative;
grid-column: 1 / -1;
z-index: 100;
}
pre.prettyprint.prettyprinted {
border: none;
position: relative;
}
pre.prettyprint.prettyprinted::after {
content: "";
position: absolute;
top: 0;
left: 0;
width: 460px;
height: 460px;
background-color: white;
border-radius: 50%;
} */
.notebook {
width: 100%;
/* margin-top: 60px; */
/* padding-top: 60px; */
/* border-top: 1px solid currentColor; */
}
.notebook h1::before {
content: "Documentation: ";
}
.notebook p {
grid-column: span 2;
text-indent: 0;
}
.notebook pre {
white-space: pre-wrap;
grid-column: 1 / -1;
display: grid;
grid-template-columns: 1fr 1fr 1fr;
grid-gap: 30px;
}
.notebook .language-python {
grid-column: 1 / span 2;
position: relative;
display: flex;
flex-direction: column;
justify-content: center;
white-space: pre;
background: none;
padding: 0;
z-index: 100;
min-height: 400px;
}
.notebook .language-python::after {
content: "";
position: absolute;
z-index: 0;
top: 0;
aspect-ratio: 1;
height: 100%;
border-radius: 50%;
background-color: white;
mix-blend-mode: color;
pointer-events: none;
}
.notebook h1 {
grid-column: 1 / -1;
}
.notebook p {
margin: 30px 0;
grid-column: 2 / span 2;
}
.notebook p img {
display: block;
}
.notebook code {
grid-column: 2 / span 2;
align-self: flex-start;
background-color: white;
padding: 12px;
border-radius: var(--radius);
}
.notebook h2 {
grid-column: 1/-1;
}
.notebook img {
margin: 30px 0;
}
.notebook > * + * {
margin-top: 30px;
}
@media (max-width: 767.98px) {
.playground,
.playground-input,
.playground-output,
.notebook,
.function-io,
.notebook pre {
display: block;
}
.function-io .input,
.function-io .output,
.playground .input {
background: none;
aspect-ratio: auto;
}
.playground-output {
margin-top: 30px;
}
.page-content > * + * {
margin-top: 60px;
}
.notebook .language-python {
min-height: 300px;
white-space: pre-wrap;
}
}

@ -0,0 +1,343 @@
html,
body {
margin: 0;
box-sizing: border-box;
background-color: var(--background);
font-family: var(--font);
font-size: var(--text);
line-height: 1.4;
overflow-x: hidden;
}
h1,
h2,
h3 {
font-family: var(--font);
font-weight: bold;
font-style: italic;
}
h1 {
font-size: var(--title);
margin: 0;
}
h2,
h3 {
font-size: var(--text);
}
a {
color: currentColor;
}
ol, ul{
padding-left: 0;
}
ul {
list-style: none;
}
ul li::before {
content: "";
}
li > ul,
li > ol,
{
padding-left: 12px;
color: red;
}
code,
pre {
font-family: "Necto";
border: none;
white-space: break-spaces;
}
input {
font-family: var(--font);
font-size: var(--text);
padding: 6px;
border: 1px solid currentColor;
border-radius: 6px;
}
button,
input[type="submit"] {
font-family: var(--font);
font-size: var(--text);
font-weight: bold;
font-style: italic;
background-color: white;
border: 1px solid currentColor;
padding: 6px;
border-radius: 12px;
transform: scale(1);
transition: transform 0.3s ease-out;
cursor: pointer;
}
button,
input[type="submit"]:active {
transform: scale(0.95);
transition: transform 0.1s ease-in;
}
/* STICKER CONTAINER */
#sticker-container {
position: absolute;
left: 0;
top: 0;
bottom: 0;
right: 0;
pointer-events: none;
z-index: 100;
min-height: 100vh;
height: 100%;
}
#sticker-fix-container {
position: fixed;
left: 0;
top: 0;
bottom: 0;
right: 0;
pointer-events: none;
z-index: 300;
min-height: 100vh;
height: 100%;
}
.sticker {
position: absolute;
user-select: none;
border: 1px solid currentColor;
border-radius: var(--radius);
white-space: nowrap;
background-color: white;
font-size: 18px;
padding: 0 6px;
box-shadow: 2px 2px 2px 1px rgba(0, 0, 0, 0.2);
text-decoration: none;
overflow-wrap: break-word;
}
*[data-sticker] {
position: relative;
}
/* HEADER AND NAV */
.page--header {
position: fixed;
z-index: 200;
top: 60px;
left: 50%;
transform: translate(-50%, 0);
width: calc(100% - 2 * var(--app-margin));
max-width: var(--content-width);
padding: 0 var(--app-margin);
display: flex;
justify-content: center;
}
header {
flex-grow: 1;
flex-shrink: 1;
display: flex;
flex-direction: column;
justify-content: center;
border: 1px solid currentColor;
border-radius: var(--radius);
background-color: var(--background);
box-shadow: 0px 2px 2px rgba(0, 0, 0, 0.25);
height: 48px; /* comment this in page with multiple elements in the header */
flex-basis: 48px;
padding: 0px 12px;
}
.page--header .title {
/* flex-grow: 0; */
/* flex-shrink: 1; */
/* line-height: 1; */
margin: 0;
}
.page--header .subtitle {
line-height: 1;
margin: 6px 0;
}
nav {
margin-left: 12px;
}
.page--header nav a {
display: inline-block;
width: 48px;
height: 48px;
border-radius: 50%;
border: 1px solid currentColor;
background-color: var(--background);
margin: 0;
}
.page--header nav a + a {
margin-left: 6px;
}
/* FOOTER */
.page--footer {
position: relative;
bottom: var(--app-margin);
width: calc(100% - 2 * var(--app-margin));
max-width: var(--content-width);
padding: 0 var(--app-margin);
left: 50%;
transform: translate(-50%, 0);
}
footer {
margin-top: 90px;
margin-bottom: 30px;
padding: 6px 12px;
border: 1px solid currentColor;
border-radius: var(--radius);
font-weight: bold;
font-style: italic;
}
/* MAIN CONTENTS */
.page--content {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
grid-column-gap: var(--app-margin);
align-items: baseline;
max-width: var(--content-width);
padding: 0 var(--app-margin);
margin: 0 auto;
margin-top: 180px;
}
.page--content p {
grid-column-end: span 2;
margin: 0;
text-indent: calc(var(--text) * 2);
line-height: 1.4;
}
.page--content h2 {
font-size: var(--title);
margin: 0;
margin-top: calc(var(--app-margin) * 3);
margin-bottom: var(--app-margin);
}
.page--content > * + * {
margin-top: var(--app-margin);
}
.page--content img {
grid-column-end: span 2;
width: 100%;
margin: 60px 0;
border-radius: var(--radius);
border: 1px solid currentColor;
}
/* GRID functional classes sorry this is not good at all but.... */
.c2 {
grid-column-end: span 2;
}
.s1 {
grid-column-start: 1;
}
.s2 {
grid-column-start: 2;
}
/* Medium screens */
@media (max-width: 991.98px) {
/* HEADER MOBILE */
.page--header {
top: 30px;
}
header {
min-height: 42px;
}
.page--header .title {
font-size: 24px;
}
.page--header .subtitle {
font-size: 21px;
}
.page--header nav a {
width: 48px;
height: 48px;
}
/* MAIN CONTENTS */
.page--content {
margin-top: 120px;
}
}
@media (max-width: 767.98px) {
.page--content {
display: block;
}
.page--content h2 {
margin-top: 60px;
}
ul, ol {
list-style-position: inside;
}
}
.highlight {
color: blue;
}

@ -0,0 +1,95 @@
.sticker {
font-size: var(--text);
pointer-events: all;
}
.index {
margin: calc(var(--app-margin) * 2);
}
.sheet + .sheet {
margin-top: var(--app-margin);
}
.sheet h3 a {
text-decoration: none;
}
.negative {
position: relative;
/* visibility: hidden; */
color: rgba(0,0,0,0.2);
text-decoration: none;
cursor: pointer;
line-height: 48px;
background-color: rgba(0, 0, 0, 0.075);
/* background-color: var(--background); */
border-radius: var(--radius);
padding: 3px 12px;
margin: 12px 0;
}
/* .negative:after {
visibility: visible;
content: "";
background-color: rgba(0, 0, 0, 0.075);
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
border-radius: var(--radius);
} */
.sheet > p {
display: inline-block;
margin: 6px 0;
}
h1.negative {
display: table;
}
.meta > h2 {
display: inline-block;
margin: 6px 0;
}
.sheet .section {
padding: 0;
margin: 0;
margin-bottom: 6px;
font-size: var(--title);
color: rgba(0, 0, 0, 0.1);
}
.chat-link {
position: absolute;
z-index: 1000;
top: 60px;
right: 60px;
background-color: springgreen;
border: 1px solid currentColor;
border-radius: 12px;
padding: 0 12px;
text-decoration: none;
font-weight: bold;
font-style: italic;
transform: scale(1);
transition: transform 0.2s ease-in;
}
.chat-link:hover{
transition: transform 0.1s ease-out;
transform: scale(1.025);
}

@ -0,0 +1,33 @@
:root {
--background: white!important;
}
.page--content p {
grid-column: 1 / span 2;
}
.bubble + .bubble {
margin-top: 30px;
}
.bubble:nth-child(2n + 2) {
/* background-color: var(--orange); */
grid-column-start: 1;
}
p.bubble {
grid-column-start: 2;
margin: 30px 0;
border: 1px solid currentColor;
padding: 12px;
border-radius: var(--radius);
text-indent: 0;
justify-self: start;
}
.page--content p.intro {
text-indent: 0;
margin-bottom: 30px;
}

@ -0,0 +1,27 @@
html,
body {
margin: 0;
font-family: 'Courier New';
width: 100%;
overflow: hidden;
background-color: rgb(180, 180, 180) ;
}
.highlight {
background-color: rgba(0, 150, 255, 0.6);
text-shadow: 0 0 0px black;
}
.lowlight {
background-color: rgba(0, 0, 255, 0);
color: black;
text-shadow: 0 0 0px black;
}
.ghost {
color: black;
text-shadow: 0 0 0px black;
}

@ -0,0 +1,37 @@
.page--content pre {
grid-column: span 2;
}
.page--content h2 {
grid-column: 1 / -1;
}
.page--content h2:first-of-type{
margin-top: 0px;
}
.page--content h3 {
grid-column: 1 / span 1;
}
.page--content ul,
.page--content ol {
grid-column: 2 / span 2;
}
.page--content li {
margin-top: 12px;
}
.page--content p {
grid-column: 2 / span 2;
}
.page--content p img {
display: block;
}

@ -0,0 +1,48 @@
.page--content .project-link {
grid-column: 1 / -1;
place-self: center;
display: inline-block;
border: 1px solid currentColor;
width: 300px;
height: 300px;
border-radius: 50%;
display: flex;
justify-content: center;
align-items: center;
text-decoration: none;
font-weight: bold;
font-style: italic;
margin: 60px 0;
}
.project-link:hover{
background-color: white;
}
.page--content .description {
grid-column: 1 / span 2;
}
.page--content pre {
grid-column: 2 / span 2;
}
.page--header header{
height: auto;
}
.page--content p {
position: relative;
}
.page--content p img {
display: block;
}

@ -0,0 +1,4 @@
.page--content p:first-of-type {
grid-column: 1 / span 2;
margin-bottom: 60px;
}

@ -0,0 +1,24 @@
@import url("/static/font/font.css");
:root {
--blue: #9edae2;
--pink: #f7d8e8;
--orange: #ffc496;
--yellow: #f9f5b0;
--green: #9fd3a8;
--content-width: 1440px;
--radius: 12px;
--background: var(--yellow);
--font: "Pirelli";
--title: 36px;
--text: 24px;
--app-margin: 30px;
}
@media (max-width: 991.98px) {
:root {
--app-margin: 18px;
--text: 21px;
}
}

@ -0,0 +1,61 @@
.cards{
grid-column: 2 / span 2;
/* display: flex;
flex-wrap: wrap;
justify-content: space-between; */
display: grid;
grid-template-columns: 1fr 1fr 1fr 1fr;
grid-gap: var(--app-margin);
}
.card-image{
position: relative;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
font-size: 16px;
font-weight: bold;
font-style: italic;
}
.card-image{
word-break: break-all;
text-align: center;
}
.cards .card-image .thumb {
width: 96px;
height: 96px;
object-fit: contain;
border: none;
border-radius: 0;
margin: 0;
}
@media (max-width: 991.98px) {
.cards {
display: block;
padding: 0;
}
.card-image{
flex-direction: row;
justify-content: flex-start;
text-align: left;
}
figure {
margin: 12px 0;
}
.cards .card-image .thumb {
width: 64px;
height: 64px;
border-radius: 0;
margin-right: 12px;
}
}

@ -0,0 +1,61 @@
.cards{
grid-column: 2 / span 2;
/* display: flex;
flex-wrap: wrap;
justify-content: space-between; */
display: grid;
grid-template-columns: 1fr 1fr 1fr 1fr;
grid-gap: var(--app-margin);
}
.card-image{
position: relative;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
font-size: 16px;
font-weight: bold;
font-style: italic;
}
.card-image{
word-break: break-all;
text-align: center;
}
.cards .card-image .thumb {
width: 96px;
height: 96px;
object-fit: contain;
border: none;
border-radius: 0;
margin: 0;
}
@media (max-width: 991.98px) {
.cards {
display: block;
padding: 0;
}
.card-image{
flex-direction: row;
justify-content: flex-start;
text-align: left;
}
figure {
margin: 12px 0;
}
.cards .card-image .thumb {
width: 64px;
height: 64px;
border-radius: 0;
margin-right: 12px;
}
}

@ -0,0 +1,322 @@
html,
body {
margin: 0;
font-family: 'Courier New';
width: 100%;
overflow: hidden;
background-color: grey ;
}
.red {
color: red;
}
.blur {
text-shadow: 0 0 8px #000;
}
.red-blur {
color: red;
text-shadow: 0 0 8px red;
text-decoration: underline;
}
.background-color {
background-color: rgba(255, 0, 0, 0.6);
text-shadow: 0 0 8px black;
}
.test-form {
position: fixed;
top: 0;
left: 0;
z-index: 500;
}
#container {
width: 100%;
height: 100vh;
background-color: #fff;
}
#editor {
position: absolute;
display: none;
border: 1px solid tomato;
opacity: 0.5;
width: 0;
height: 0;
z-index: 100;
}
#editor.can-draw {
opacity: 1;
}
#editor.show-editor {
display: block;
}
.label {
position: absolute;
overflow: hidden;
}
.label.temporary {
background: none;
/* border: 1px dashed tomato; */
box-shadow: none;
overflow: visible;
}
.label.temporary form {
width: 100%;
height: 100%;
}
.label.temporary textarea {
width: 100%;
height: 100%;
padding: 1ch;
border: none;
background-color: rgba(255, 255, 255, 0.6);
}
.label.temporary textarea:focus {
outline: 1px dashed tomato;
}
.label.temporary button {
margin-top: 4px;
}
.label.temporary button + button {
margin-left: 4px;
}
.label.temporary .label--number,
.label.temporary .label--close {
display: none;
}
.label--number {
display: inline-block;
margin: 0;
padding: 0 4px;
user-select: none;
background-color: white;
}
.label--close {
position: absolute;
right: 0;
border: none;
padding: 0 4px;
font-size: 1rem;
background: none;
color: white;
cursor: pointer;
}
.label--text {
margin: 1ch 0;
padding: 0 1ch;
overflow: hidden;
width: 100%;
height: 100%;
text-overflow: ellipsis;
white-space: pre;
}
.text-input {
display: none;
position: absolute;
z-index: 200;
width: 100%;
height: 100vh;
justify-content: center;
align-items: center;
background-color: rgba(255, 99, 71, 0.95);
}
.text-input.visible {
display: flex;
}
.modal {
padding: 64px;
}
.modal input {
font-size: 1.5rem;
background: none;
border: none;
color: white;
border-bottom: 1px solid white;
}
.modal input:focus {
outline: none;
background-color: rgba(255, 255, 255, 0.25);
}
.text-input button {
color: white;
font-weight: bold;
background: none;
border: none;
cursor: pointer;
font-size: 1.5rem;
}
#cancel {
font-weight: normal;
}
.background-container {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
display: flex;
flex-direction: column;
}
.background-container img {
max-width: 70w;
max-height: 70vh;
object-fit: contain;
user-select: none;
pointer-events: none;
}
.background-container img.visible {
display: initial;
}
.hidden {
display: none;
}
.info,
.transcription {
position: absolute;
right: 0;
bottom: 0;
top: 0;
z-index: 50;
padding: 24px;
margin: 0;
width: 25%;
line-height: 1.6;
background-color: #111;
color: white;
transform: translateX(100%);
transition: transform 0.4s ease-out;
}
.transcription.active,
.info.active {
transform: translateX(0);
transition: transform 0.6s ease-in;
}
.transcription .title,
.info .title {
margin: 0;
}
.transcription ol {
padding: 0;
list-style-position: inside;
font-size: 1.125rem;
}
#show-info,
#show-transcription,
.close,
button {
background: none;
display: inline-block;
min-width: 24px;
height: 24px;
border-radius: 24px;
padding: 0 4px;
border: 1px solid currentColor;
color: tomato;
cursor: pointer;
}
#show-transcription:hover,
#show-info:hover {
border: 1px solid tomato;
background-color: tomato;
color: white;
}
.close {
position: absolute;
right: 24px;
top: 32px;
color: white;
}
#export-text:hover,
.close:hover {
border: 1px solid white;
background-color: white;
color: #111;
}
#export-text {
color: white;
}
nav {
position: absolute;
top: 0;
left: 0;
right: 0;
z-index: 50;
padding: 24px;
text-align: right;
pointer-events: none;
}
nav > * {
pointer-events: all;
}
img.hidden {
display: none;
}

@ -0,0 +1,337 @@
html,
body {
margin: 0;
font-family: Arial, Helvetica, sans-serif;
width: 100%;
overflow: hidden;
}
.test-form {
position: fixed;
top: 0;
left: 0;
z-index: 500;
}
#container {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
width: 90vw;
height: 90vh;
padding: 0;
}
#editor {
position: absolute;
display: none;
border: 1px solid tomato;
opacity: 0.5;
width: 0;
height: 0;
top: 0;
left: 0;
z-index: 150;
pointer-events: none;
}
#editor.can-draw {
opacity: 1;
}
#editor.show-editor {
display: block;
}
.label {
position: absolute;
background-color: rgba(250, 99, 72, 0.2);
/* border: 1px solid currentColor; */
box-shadow: 0px 0px 8px 0px rgba(0, 0, 0, 0.2);
overflow: hidden;
}
.label.temporary {
background: none;
/* border: 1px dashed tomato; */
box-shadow: none;
overflow: visible;
}
.label.temporary form {
width: 100%;
height: 100%;
}
.label.temporary textarea {
width: 100%;
height: 100%;
padding: 1ch;
border: none;
background-color: rgba(255, 255, 255, 0.6);
}
.label.temporary textarea:focus {
outline: 1px dashed tomato;
}
.label.temporary button {
margin-top: 4px;
}
.label.temporary button + button {
margin-left: 4px;
}
.label.temporary .label--number,
.label.temporary .label--close {
display: none;
}
.label--number {
display: inline-block;
margin: 0;
padding: 0 4px;
user-select: none;
background-color: white;
}
.label--close {
position: absolute;
right: 0;
border: none;
padding: 0 4px;
font-size: 1rem;
background: none;
color: white;
cursor: pointer;
}
.label--text {
margin: 1ch 0;
padding: 0 1ch;
overflow: hidden;
width: 100%;
height: 100%;
text-overflow: ellipsis;
overflow-y: auto;
white-space: pre-line;
}
.text-input {
display: none;
position: absolute;
z-index: 200;
width: 100%;
height: 100vh;
justify-content: center;
align-items: center;
background-color: rgba(255, 99, 71, 0.95);
}
.text-input.visible {
display: flex;
}
.modal input {
font-size: 1.5rem;
background: none;
border: none;
color: white;
border-bottom: 1px solid white;
}
.modal input:focus {
outline: none;
background-color: rgba(255, 255, 255, 0.25);
}
.text-input button {
color: white;
font-weight: bold;
background: none;
border: none;
cursor: pointer;
font-size: 1.5rem;
}
#cancel {
font-weight: normal;
}
.background-container {
width: 100%;
height: 100%;
margin: 0;
}
#target {
position: absolute;
top: 0;
left: 0;
width: 0;
height: 0;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
}
.background-container img {
width: auto;
height: auto;
max-width: 100%;
max-height: 100%;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
object-fit: contain;
user-select: none;
position: relative;
pointer-events: none;
}
.background-container img.visible {
display: initial;
}
.hidden {
display: none;
}
.info,
.transcription {
position: absolute;
right: 0;
bottom: 0;
top: 0;
z-index: 50;
margin: 0;
width: min(50%, 600px);
line-height: 1.6;
background-color: #111;
color: white;
transform: translateX(100%);
transition: transform 0.4s ease-out;
}
.info .contents,
.transcription .contents {
padding: 24px;
height: 100vh;
overflow-y: auto;
}
.contents p {
white-space: pre-line;
}
.transcription.active,
.info.active {
transform: translateX(0);
transition: transform 0.6s ease-in;
}
.transcription .title,
.info .title {
margin: 0;
}
.transcription ol {
padding: 0;
list-style-position: inside;
font-size: 1.125rem;
margin-bottom: 100px;
}
#show-info,
#show-transcription,
.close,
button {
background: none;
display: inline-block;
min-width: 24px;
height: 24px;
border-radius: 24px;
padding: 0 4px;
border: 1px solid currentColor;
color: tomato;
cursor: pointer;
}
#show-transcription:hover,
#show-info:hover {
border: 1px solid tomato;
background-color: tomato;
color: white;
}
.close {
position: absolute;
right: 24px;
top: 32px;
color: white;
}
#export-text:hover,
.close:hover {
border: 1px solid white;
background-color: white;
color: #111;
}
#export-text {
color: white;
}
nav {
position: absolute;
top: 0;
left: 0;
right: 0;
z-index: 50;
padding: 24px;
text-align: right;
pointer-events: none;
}
nav > * {
pointer-events: all;
}
img.hidden {
display: none;
}
.transcription .options {
margin-top: 24px;
}

@ -0,0 +1,233 @@
h3,
h2 {
margin-top: 0;
}
.description {
grid-column: 1 / span 2;
}
.function-io,
.endpoints,
.playground {
margin: 30px 0;
}
.playground,
.playground-input,
.playground-output,
.notebook {
grid-column: -1 / 1;
display: grid;
grid-template-columns: 1fr 1fr 1fr;
grid-column-gap: 30px;
}
.function-io {
grid-column: 1 / -1;
display: grid;
grid-template-columns: 1fr 1fr 1fr;
place-items: start;
grid-gap: 30px;
}
.function-io > * {
width: 100%;
height: 100%;
}
.function-io .input,
.function-io .output,
.playground .input {
background-color: white;
aspect-ratio: 1;
border-radius: 50%;
}
.function-io h3 {
margin: 0;
}
.function-io ul {
font-family: "Necto";
list-style: none;
padding: 0;
}
.endpoints {
grid-column: 1 / -1;
}
.endpoints code {
background-color: white;
margin: 30px 0;
white-space: initial;
}
.endpoints .example {
margin: 15px 0;
}
.playgroud-input .input {
grid-column: 1 / span 1;
align-self: center;
}
.playground-output .output {
grid-column: 2 / span 2;
background-color: white;
border-radius: 12px;
padding: 12px;
min-height: calc(var(--text) * 2);
}
.playground label {
display: block;
}
.playground * + label {
margin-top: 24px;
margin-bottom: 6px;
}
.playground input[type="submit"] {
margin-top: 30px;
}
/* .notebook code {
position: relative;
grid-column: 1 / -1;
z-index: 100;
}
pre.prettyprint.prettyprinted {
border: none;
position: relative;
}
pre.prettyprint.prettyprinted::after {
content: "";
position: absolute;
top: 0;
left: 0;
width: 460px;
height: 460px;
background-color: white;
border-radius: 50%;
} */
.notebook {
width: 100%;
/* margin-top: 60px; */
/* padding-top: 60px; */
/* border-top: 1px solid currentColor; */
}
.notebook h1::before {
content: "Documentation: ";
}
.notebook p {
grid-column: span 2;
text-indent: 0;
}
.notebook pre {
white-space: pre-wrap;
grid-column: 1 / -1;
display: grid;
grid-template-columns: 1fr 1fr 1fr;
grid-gap: 30px;
}
.notebook .language-python {
grid-column: 1 / span 2;
position: relative;
display: flex;
flex-direction: column;
justify-content: center;
white-space: pre;
background: none;
padding: 0;
z-index: 100;
min-height: 400px;
}
.notebook .language-python::after {
content: "";
position: absolute;
z-index: 0;
top: 0;
aspect-ratio: 1;
height: 100%;
border-radius: 50%;
background-color: white;
mix-blend-mode: color;
pointer-events: none;
}
.notebook h1 {
grid-column: 1 / -1;
}
.notebook p {
margin: 30px 0;
grid-column: 2 / span 2;
}
.notebook p img {
display: block;
}
.notebook code {
grid-column: 2 / span 2;
align-self: flex-start;
background-color: white;
padding: 12px;
border-radius: var(--radius);
}
.notebook h2 {
grid-column: 1/-1;
}
.notebook img {
margin: 30px 0;
}
.notebook > * + * {
margin-top: 30px;
}
@media (max-width: 767.98px) {
.playground,
.playground-input,
.playground-output,
.notebook,
.function-io,
.notebook pre {
display: block;
}
.function-io .input,
.function-io .output,
.playground .input {
background: none;
aspect-ratio: auto;
}
.playground-output {
margin-top: 30px;
}
.page-content > * + * {
margin-top: 60px;
}
.notebook .language-python {
min-height: 300px;
white-space: pre-wrap;
}
}

@ -0,0 +1,343 @@
html,
body {
margin: 0;
box-sizing: border-box;
background-color: var(--background);
font-family: var(--font);
font-size: var(--text);
line-height: 1.4;
overflow-x: hidden;
}
h1,
h2,
h3 {
font-family: var(--font);
font-weight: bold;
font-style: italic;
}
h1 {
font-size: var(--title);
margin: 0;
}
h2,
h3 {
font-size: var(--text);
}
a {
color: currentColor;
}
ol, ul{
padding-left: 0;
}
ul {
list-style: none;
}
ul li::before {
content: "";
}
li > ul,
li > ol,
{
padding-left: 12px;
color: red;
}
code,
pre {
font-family: "Necto";
border: none;
white-space: break-spaces;
}
input {
font-family: var(--font);
font-size: var(--text);
padding: 6px;
border: 1px solid currentColor;
border-radius: 6px;
}
button,
input[type="submit"] {
font-family: var(--font);
font-size: var(--text);
font-weight: bold;
font-style: italic;
background-color: white;
border: 1px solid currentColor;
padding: 6px;
border-radius: 12px;
transform: scale(1);
transition: transform 0.3s ease-out;
cursor: pointer;
}
button,
input[type="submit"]:active {
transform: scale(0.95);
transition: transform 0.1s ease-in;
}
/* STICKER CONTAINER */
#sticker-container {
position: absolute;
left: 0;
top: 0;
bottom: 0;
right: 0;
pointer-events: none;
z-index: 100;
min-height: 100vh;
height: 100%;
}
#sticker-fix-container {
position: fixed;
left: 0;
top: 0;
bottom: 0;
right: 0;
pointer-events: none;
z-index: 300;
min-height: 100vh;
height: 100%;
}
.sticker {
position: absolute;
user-select: none;
border: 1px solid currentColor;
border-radius: var(--radius);
white-space: nowrap;
background-color: white;
font-size: 18px;
padding: 0 6px;
box-shadow: 2px 2px 2px 1px rgba(0, 0, 0, 0.2);
text-decoration: none;
overflow-wrap: break-word;
}
*[data-sticker] {
position: relative;
}
/* HEADER AND NAV */
.page--header {
position: fixed;
z-index: 200;
top: 60px;
left: 50%;
transform: translate(-50%, 0);
width: calc(100% - 2 * var(--app-margin));
max-width: var(--content-width);
padding: 0 var(--app-margin);
display: flex;
justify-content: center;
}
header {
flex-grow: 1;
flex-shrink: 1;
display: flex;
flex-direction: column;
justify-content: center;
border: 1px solid currentColor;
border-radius: var(--radius);
background-color: var(--background);
box-shadow: 0px 2px 2px rgba(0, 0, 0, 0.25);
height: 48px; /* comment this in page with multiple elements in the header */
flex-basis: 48px;
padding: 0px 12px;
}
.page--header .title {
/* flex-grow: 0; */
/* flex-shrink: 1; */
/* line-height: 1; */
margin: 0;
}
.page--header .subtitle {
line-height: 1;
margin: 6px 0;
}
nav {
margin-left: 12px;
}
.page--header nav a {
display: inline-block;
width: 48px;
height: 48px;
border-radius: 50%;
border: 1px solid currentColor;
background-color: var(--background);
margin: 0;
}
.page--header nav a + a {
margin-left: 6px;
}
/* FOOTER */
.page--footer {
position: relative;
bottom: var(--app-margin);
width: calc(100% - 2 * var(--app-margin));
max-width: var(--content-width);
padding: 0 var(--app-margin);
left: 50%;
transform: translate(-50%, 0);
}
footer {
margin-top: 90px;
margin-bottom: 30px;
padding: 6px 12px;
border: 1px solid currentColor;
border-radius: var(--radius);
font-weight: bold;
font-style: italic;
}
/* MAIN CONTENTS */
.page--content {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
grid-column-gap: var(--app-margin);
align-items: baseline;
max-width: var(--content-width);
padding: 0 var(--app-margin);
margin: 0 auto;
margin-top: 180px;
}
.page--content p {
grid-column-end: span 2;
margin: 0;
text-indent: calc(var(--text) * 2);
line-height: 1.4;
}
.page--content h2 {
font-size: var(--title);
margin: 0;
margin-top: calc(var(--app-margin) * 3);
margin-bottom: var(--app-margin);
}
.page--content > * + * {
margin-top: var(--app-margin);
}
.page--content img {
grid-column-end: span 2;
width: 100%;
margin: 60px 0;
border-radius: var(--radius);
border: 1px solid currentColor;
}
/* GRID functional classes sorry this is not good at all but.... */
.c2 {
grid-column-end: span 2;
}
.s1 {
grid-column-start: 1;
}
.s2 {
grid-column-start: 2;
}
/* Medium screens */
@media (max-width: 991.98px) {
/* HEADER MOBILE */
.page--header {
top: 30px;
}
header {
min-height: 42px;
}
.page--header .title {
font-size: 24px;
}
.page--header .subtitle {
font-size: 21px;
}
.page--header nav a {
width: 48px;
height: 48px;
}
/* MAIN CONTENTS */
.page--content {
margin-top: 120px;
}
}
@media (max-width: 767.98px) {
.page--content {
display: block;
}
.page--content h2 {
margin-top: 60px;
}
ul, ol {
list-style-position: inside;
}
}
.highlight {
color: blue;
}

@ -0,0 +1,99 @@
.sticker {
font-size: var(--text);
pointer-events: all;
}
.index {
margin: calc(var(--app-margin) * 2);
}
.sheet + .sheet {
margin-top: var(--app-margin);
}
.sheet h3 a {
text-decoration: none;
}
.negative {
position: relative;
/* visibility: hidden; */
color: rgba(0,0,0,0.2);
text-decoration: none;
cursor: pointer;
line-height: 48px;
background-color: rgba(0, 0, 0, 0.075);
/* background-color: var(--background); */
border-radius: var(--radius);
padding: 3px 12px;
margin: 12px 0;
}
/* .negative:after {
visibility: visible;
content: "";
background-color: rgba(0, 0, 0, 0.075);
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
border-radius: var(--radius);
} */
.sheet > p {
display: inline-block;
margin: 6px 0;
}
h1.negative {
display: table;
}
.meta > h2 {
display: inline-block;
margin: 6px 0;
}
.sheet .section {
padding: 0;
margin: 0;
margin-bottom: 6px;
font-size: var(--title);
color: rgba(0, 0, 0, 0.1);
}
.chat-link {
position: absolute;
z-index: 1000;
top: 60px;
right: 60px;
background-color: springgreen;
border: 1px solid currentColor;
border-radius: 12px;
padding: 0 12px;
text-decoration: none;
font-weight: bold;
font-style: italic;
transform: scale(1);
transition: transform 0.2s ease-in;
}
.chat-link:hover{
transition: transform 0.1s ease-out;
transform: scale(1.025);
}

@ -0,0 +1,33 @@
:root {
--background: white!important;
}
.page--content p {
grid-column: 1 / span 2;
}
.bubble + .bubble {
margin-top: 30px;
}
.bubble:nth-child(2n + 2) {
/* background-color: var(--orange); */
grid-column-start: 1;
}
p.bubble {
grid-column-start: 2;
margin: 30px 0;
border: 1px solid currentColor;
padding: 12px;
border-radius: var(--radius);
text-indent: 0;
justify-self: start;
}
.page--content p.intro {
text-indent: 0;
margin-bottom: 30px;
}

@ -0,0 +1,27 @@
html,
body {
margin: 0;
font-family: 'Courier New';
width: 100%;
overflow: hidden;
background-color: rgb(180, 180, 180) ;
}
.highlight {
background-color: rgba(0, 150, 255, 0.6);
text-shadow: 0 0 0px black;
}
.lowlight {
background-color: rgba(0, 0, 255, 0);
color: black;
text-shadow: 0 0 0px black;
}
.ghost {
color: black;
text-shadow: 0 0 0px black;
}

@ -0,0 +1,73 @@
.list {
grid-column: 1 / -1;
width: 100%;
margin-top: 60px;
}
table {
border-collapse: collapse;
}
tr {
position: relative;
border-top: 1px solid currentColor;
display: grid;
grid-template-columns: 1fr 1fr 1fr;
grid-gap: 30px;
}
tr:last-of-type {
padding-bottom: 24px;
border-bottom: 1px solid currentColor;
}
td {
padding: 24px 0;
}
td.title {
font-weight: bold;
font-style: italic;
}
td.description {
grid-column: 2 / span 2;
padding-right: 90px;
}
td.link {
position: absolute;
right: 0;
text-align: right;
}
@media (max-width: 767.98px) {
table,
tr,
td {
display: block;
}
tr + tr {
margin-top: 24px;
}
td {
padding: 0;
}
td.title {
padding-right: 30px;
margin-top: 18px;
margin-bottom: 6px;
}
td.description {
padding-right: 0;
}
td.link {
top: 18px;
right: 0;
}
}

@ -0,0 +1,37 @@
body {
padding: 16px;
}
.page--header {
width: auto;
top: 45px;
margin: 0;
padding: 0;
}
.sections {
margin-top: 30vh;
display: flex;
flex-direction: column-reverse;
color: var(--textcolor);
row-gap:4vh;
}
.section {
position:relative
}
.reporter {
align-self: flex-start;
width: 70vw;
}
.answer {
align-self: flex-end;
width: 70vw;
text-align: right;
}
@media (max-width: 991.98px) {
.reporter{
width:100%;
}
.answer{
width:100%;
}
}

@ -0,0 +1,7 @@
// Get the button, and when the user clicks on it, execute myFunction
//document.getElementById("info-btn").onclick = function() {showInfo()};
/* myFunction toggles between adding and removing the show class, which is used to hide and show the dropdown content */
function showInfo() {
document.getElementById("info").classList.toggle("show");
}

@ -0,0 +1,270 @@
@font-face {
font-family: Pirelli;
src: url("./font/pirelli-regular.woff");
}
@font-face {
font-family: Pirelli;
src: url("./font/pirelli-bolditalic.woff");
font-weight: bold;
}
@font-face {
font-family: Necto-Mono;
src: url("./font/Necto-Mono.woff");
}
:root {
--red: tomato;
--background: rgb(233, 233, 233);
--textcolor: rgb(39, 39, 39);
--blue: #9edae2;
--pink: #f7d8e8;
--orange: #ffc496;
--yellow: #f9f5b0;
--green: #9fd3a8;
--content-width: 1440px;
--radius: 12px;
--font: "Pirelli";
--title: 36px;
--text: 24px;
--app-margin: 30px;
}
html {
scroll-behavior: smooth;
overflow-x: hidden;
}
body {
font-family: Pirelli;
background-color: var(--background);
color: var(--textcolor);
margin: 0;
}
* {
box-sizing: border-box;
}
a.goto-archive {
position: fixed;
top: 16px;
right: 16px;
color: var(--red);
font-size: 1.2em;
z-index: 1;
}
.page--header {
top: 0;
width: 100%;
margin: 8vh 0;
padding: 0 16px;
display: flex;
flex-direction: row;
column-gap: 8px;
align-items: stretch;
justify-content: center;
position: fixed;
z-index: 1;
}
.page--header > * {
border: solid 1px var(--red);
border-radius: 8px;
padding: 12px;
background-color: var(--background);
}
header {
flex-grow: 1;
}
nav {
cursor: pointer;
}
h1 {
font-size: 36px;
color: var(--red);
margin: 0;
font-weight: normal;
}
.question {
display: flex;
font-size: 1.2em;
flex-direction: column;
flex-grow: 1;
justify-content: space-between;
align-items: flex-start;
row-gap: 24px;
width: 100%;
margin-top: 28vh;
padding: 0 16px;
}
.info {
/*background-color: white;*/
display: none;
padding: 16px;
border: solid 1px var(--red);
border-radius: 8px;
color: var(--red);
font-size: 1em;
flex-basis: 35%;
}
.show {
display: block;
}
.q-text {
margin-bottom: 1em;
flex-basis: 65%;
}
/*styling of form*/
form {
padding: 16px;
display: flex;
flex-flow: wrap;
column-gap: 24px;
row-gap: 16px;
align-items: flex-start;
}
.form-box {
display: flex;
flex-direction: column;
row-gap: 8px;
flex-basis: 30%;
flex-grow: 1;
}
.form-box#apply {
align-self: flex-end;
}
textarea {
resize: none;
/* font-family: Necto-mono; */
border-radius: 4px;
border: solid 1px grey;
transition: border-radius 70ms ease-in-out;
}
/* when the cursor is iside the text area*/
textarea:focus {
border: solid 2px var(--red);
outline: none;
border-radius: 16px;
}
input {
font-family: Pirelli;
width: 100%;
font-size: 1.2em;
margin: 0;
}
input:hover {
color: var(--red);
}
/*to pick the exact type of input you wanna style use "[]"*/
input[type="radio"] {
font-family: Pirelli;
background-color: var(--red);
}
label {
font-size: 0.9em;
}
/*custom radio button*/
/* The container */
.radio-label {
display: block;
position: relative;
padding-left: 35px;
margin-bottom: 12px;
cursor: pointer;
font-size: 1.2em;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
/* Hide the browser's default radio button */
.radio-label input {
position: absolute;
opacity: 0;
cursor: pointer;
}
/* Create a custom radio button */
.checkmark {
position: absolute;
top: 0;
left: 0;
height: 24px;
width: 24px;
background-color: rgb(255, 255, 255);
border: solid 2px var(--red);
border-radius: 50%;
}
/* On mouse-over change color */
.radio-label:hover {
color: var(--red);
}
/* When the radio button is checked, add a blue background */
.radio-label input:checked ~ .checkmark {
background-color: var(--red);
}
/* Create the indicator (the dot/circle - hidden when not checked) */
.checkmark:after {
content: "";
position: absolute;
display: none;
}
.lbl-result {
color: grey;
font-size: 0.8em;
}
.result {
font-family: Courier;
}
.form-end {
margin: 16px;
display: flex;
flex-direction: row;
flex-flow: nowrap;
align-items: center;
align-content: stretch;
column-gap: 8px;
}
.form-end > * {
flex-grow: 1;
flex-basis: 50%;
margin: 0;
padding: 0;
}
a.goto-form {
border: solid 1px rgb(107, 107, 107);
border-radius: 4px;
font-family: Pirelli;
width: 100%;
font-size: 1.2em;
text-decoration: none;
color: var(--textcolor);
text-align: center;
padding: 2px;
}
.goto-form:hover {
color: var(--red);
background-color: rgb(216, 216, 216);
}
.save-btn {
height: 100%;
}
@media (max-width: 991.98px) {
:root {
--app-margin: 18px;
--text: 21px;
}
.title,
nav.h1 {
font-size: 1.5em;
}
}

@ -0,0 +1,37 @@
.page--content pre {
grid-column: span 2;
}
.page--content h2 {
grid-column: 1 / -1;
}
.page--content h2:first-of-type{
margin-top: 0px;
}
.page--content h3 {
grid-column: 1 / span 1;
}
.page--content ul,
.page--content ol {
grid-column: 2 / span 2;
}
.page--content li {
margin-top: 12px;
}
.page--content p {
grid-column: 2 / span 2;
}
.page--content p img {
display: block;
}

@ -0,0 +1,48 @@
.page--content .project-link {
grid-column: 1 / -1;
place-self: center;
display: inline-block;
border: 1px solid currentColor;
width: 300px;
height: 300px;
border-radius: 50%;
display: flex;
justify-content: center;
align-items: center;
text-decoration: none;
font-weight: bold;
font-style: italic;
margin: 60px 0;
}
.project-link:hover{
background-color: white;
}
.page--content .description {
grid-column: 1 / span 2;
}
.page--content pre {
grid-column: 2 / span 2;
}
.page--header header{
height: auto;
}
.page--content p {
position: relative;
}
.page--content p img {
display: block;
}

@ -0,0 +1,4 @@
.page--content p:first-of-type {
grid-column: 1 / span 2;
margin-bottom: 60px;
}

@ -0,0 +1,24 @@
@import url("/static/font/font.css");
:root {
--blue: #9edae2;
--pink: #f7d8e8;
--orange: #ffc496;
--yellow: #f9f5b0;
--green: #9fd3a8;
--content-width: 1440px;
--radius: 12px;
--background: var(--yellow);
--font: "Pirelli";
--title: 36px;
--text: 24px;
--app-margin: 30px;
}
@media (max-width: 991.98px) {
:root {
--app-margin: 18px;
--text: 21px;
}
}

@ -0,0 +1,6 @@
{
"cells": [],
"metadata": {},
"nbformat": 4,
"nbformat_minor": 5
}

@ -0,0 +1,58 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "817008c8-6cc9-4e39-9ec0-b73785756099",
"metadata": {},
"source": [
"Upss I am having a break right now, cuddling my cat."
]
},
{
"cell_type": "markdown",
"id": "a39983dd-f828-499b-9e30-39ec45c056a1",
"metadata": {},
"source": [
"I wasn't ready for this connection.\n",
"I am a bit moody right now, so I am concentrating on finding my center."
]
},
{
"cell_type": "markdown",
"id": "3b79c6c1-c81a-459d-abbc-2a36bda652b8",
"metadata": {},
"source": [
"Ah! you are here! I wasn't excepting of you! Wait to put something on me"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "00670416-1257-442e-b55c-2a93b87f5931",
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.7.3"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save