Update src/streamlit_app.py
Browse files- src/streamlit_app.py +153 -34
src/streamlit_app.py
CHANGED
|
@@ -1,40 +1,159 @@
|
|
| 1 |
-
|
| 2 |
-
import numpy as np
|
| 3 |
-
import pandas as pd
|
| 4 |
import streamlit as st
|
|
|
|
|
|
|
|
|
|
| 5 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 6 |
"""
|
| 7 |
-
|
| 8 |
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 12 |
|
| 13 |
-
|
| 14 |
-
"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 15 |
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
|
| 22 |
-
|
| 23 |
-
|
| 24 |
-
y = radius * np.sin(theta)
|
| 25 |
-
|
| 26 |
-
df = pd.DataFrame({
|
| 27 |
-
"x": x,
|
| 28 |
-
"y": y,
|
| 29 |
-
"idx": indices,
|
| 30 |
-
"rand": np.random.randn(num_points),
|
| 31 |
-
})
|
| 32 |
-
|
| 33 |
-
st.altair_chart(alt.Chart(df, height=700, width=700)
|
| 34 |
-
.mark_point(filled=True)
|
| 35 |
-
.encode(
|
| 36 |
-
x=alt.X("x", axis=None),
|
| 37 |
-
y=alt.Y("y", axis=None),
|
| 38 |
-
color=alt.Color("idx", legend=None, scale=alt.Scale()),
|
| 39 |
-
size=alt.Size("rand", legend=None, scale=alt.Scale(range=[1, 150])),
|
| 40 |
-
))
|
|
|
|
| 1 |
+
# app.py
|
|
|
|
|
|
|
| 2 |
import streamlit as st
|
| 3 |
+
import random
|
| 4 |
+
from datetime import datetime
|
| 5 |
+
import json
|
| 6 |
|
| 7 |
+
st.set_page_config(page_title="Super Diverse Spooky Generator", layout="centered")
|
| 8 |
+
st.title("🕯️ Super Diverse Spooky Fictional Status Generator")
|
| 9 |
+
st.markdown(
|
| 10 |
+
"""
|
| 11 |
+
Generates **fictional, spooky profiles** with highly diverse narratives for storytelling, games, or ARGs.
|
| 12 |
+
All entries are **entirely fictional** — do **not** enter real names.
|
| 13 |
"""
|
| 14 |
+
)
|
| 15 |
|
| 16 |
+
# Input form
|
| 17 |
+
with st.form("lookup_form"):
|
| 18 |
+
name = st.text_input("Name (fictional)", placeholder="e.g., 'Evelyn Crowe'")
|
| 19 |
+
mood = st.selectbox(
|
| 20 |
+
"Tone / Mood",
|
| 21 |
+
["mysterious", "creepy", "melancholy", "absurd", "bureaucratic", "occult"]
|
| 22 |
+
)
|
| 23 |
+
seed_input = st.text_input("Seed (optional)")
|
| 24 |
+
confirm_fictional = st.checkbox(
|
| 25 |
+
"I confirm this is fictional content and not a real-person lookup", value=False
|
| 26 |
+
)
|
| 27 |
+
submitted = st.form_submit_button("Generate Profile")
|
| 28 |
|
| 29 |
+
# Variable lists for maximum diversity
|
| 30 |
+
STATUSES = ["Alive", "Deceased", "Hospitalized", "Missing", "Unknown"]
|
| 31 |
+
LOCATIONS = [
|
| 32 |
+
"a foggy lighthouse", "the abandoned metro station", "an empty library",
|
| 33 |
+
"a deserted mansion", "shadowy forest", "a forgotten cathedral",
|
| 34 |
+
"an underground laboratory", "a cursed observatory"
|
| 35 |
+
]
|
| 36 |
+
ACTIONS = [
|
| 37 |
+
"wandering aimlessly", "scribbling cryptic notes", "watching the shadows",
|
| 38 |
+
"examining strange artifacts", "listening to whispers", "staring at the void"
|
| 39 |
+
]
|
| 40 |
+
ADJECTIVES = [
|
| 41 |
+
"mysterious", "unsettling", "enigmatic", "peculiar", "ominous",
|
| 42 |
+
"arcane", "melancholy", "foreboding", "cryptic"
|
| 43 |
+
]
|
| 44 |
+
CAUSES_DEATH = [
|
| 45 |
+
"a shadowy fever", "a forbidden ritual gone wrong",
|
| 46 |
+
"venturing into cursed ruins", "an unexplained laboratory incident",
|
| 47 |
+
"consuming a mysterious elixir", "a pact with unknown forces"
|
| 48 |
+
]
|
| 49 |
+
CAUSES_SICK = [
|
| 50 |
+
"a spectral affliction", "a nocturnal illness",
|
| 51 |
+
"experimental treatment", "an unknown contagion", "a creeping malaise"
|
| 52 |
+
]
|
| 53 |
+
CAUSES_MISC = [
|
| 54 |
+
"departed on a secretive expedition", "vanished while investigating anomalies",
|
| 55 |
+
"chose seclusion in remote areas", "was last seen performing an unusual experiment"
|
| 56 |
+
]
|
| 57 |
+
TWISTS = [
|
| 58 |
+
"Records are inconsistent and partially classified.",
|
| 59 |
+
"Some witnesses claim seeing ghostly figures nearby.",
|
| 60 |
+
"Surveillance footage is mysteriously missing.",
|
| 61 |
+
"Local authorities refused to comment.",
|
| 62 |
+
"Rumors circulate of strange symbols appearing around the location.",
|
| 63 |
+
"Reports are heavily redacted."
|
| 64 |
+
]
|
| 65 |
+
BUREAUCRATIC_NOTES = [
|
| 66 |
+
"Subject is under review.", "Status pending further verification.",
|
| 67 |
+
"Classification: Confidential.", "Archived in restricted files.",
|
| 68 |
+
"Update logs incomplete.", "Cross-departmental investigation ongoing."
|
| 69 |
+
]
|
| 70 |
+
|
| 71 |
+
# Templates per status
|
| 72 |
+
TEMPLATES = {
|
| 73 |
+
"Alive": [
|
| 74 |
+
"{name} is alive, currently {action} in {location}. Observers describe the scene as {adjective}. {twist} {bureaucratic}",
|
| 75 |
+
"{name} continues to exist in {location}, appearing {adjective} while {action}. {twist}",
|
| 76 |
+
"Currently, {name} is reported alive. Location: {location}. Activity: {action}. Tone: {adjective}. {bureaucratic}"
|
| 77 |
+
],
|
| 78 |
+
"Deceased": [
|
| 79 |
+
"{name} is deceased due to {cause}. Last known location: {location}. Circumstances were {adjective}. {twist} {bureaucratic}",
|
| 80 |
+
"{name} passed away under {adjective} conditions; cause: {cause}. {twist}",
|
| 81 |
+
"Official reports note {name}'s death as {cause}. Observers found {location} unusually quiet. {bureaucratic}"
|
| 82 |
+
],
|
| 83 |
+
"Hospitalized": [
|
| 84 |
+
"{name} is hospitalized for {cause}. Current environment: {location}, atmosphere {adjective}. {twist} {bureaucratic}",
|
| 85 |
+
"{name} recovers in {location}, suffering from {cause}. {bureaucratic}",
|
| 86 |
+
],
|
| 87 |
+
"Missing": [
|
| 88 |
+
"{name} went missing while {action} in {location}. Reports are {adjective}. {twist} {bureaucratic}",
|
| 89 |
+
"Status unknown: {name} disappeared under {adjective} circumstances. Last seen at {location}. {twist}"
|
| 90 |
+
],
|
| 91 |
+
"Unknown": [
|
| 92 |
+
"No records for {name}. Current status is {adjective}. {twist} {bureaucratic}",
|
| 93 |
+
"{name}'s activities are unverified. Location: {location}. {bureaucratic}"
|
| 94 |
+
]
|
| 95 |
+
}
|
| 96 |
+
|
| 97 |
+
# Helper functions
|
| 98 |
+
def safe_name(name: str):
|
| 99 |
+
if not name or name.strip() == "":
|
| 100 |
+
return f"Subject-{random.randint(1000,9999)}"
|
| 101 |
+
name = name.strip()
|
| 102 |
+
return name
|
| 103 |
+
|
| 104 |
+
def generate_profile(name, mood, seed, confirm_fictional):
|
| 105 |
+
if not confirm_fictional:
|
| 106 |
+
return {"error": "You must confirm this lookup is fictional."}
|
| 107 |
+
|
| 108 |
+
if seed:
|
| 109 |
+
try:
|
| 110 |
+
random.seed(int(seed))
|
| 111 |
+
except:
|
| 112 |
+
pass
|
| 113 |
+
|
| 114 |
+
name = safe_name(name)
|
| 115 |
+
status = random.choice(STATUSES)
|
| 116 |
+
location = random.choice(LOCATIONS)
|
| 117 |
+
action = random.choice(ACTIONS)
|
| 118 |
+
adjective = random.choice(ADJECTIVES)
|
| 119 |
+
cause = random.choice(CAUSES_DEATH if status=="Deceased" else
|
| 120 |
+
CAUSES_SICK if status=="Hospitalized" else
|
| 121 |
+
CAUSES_MISC if status=="Missing" else ["unknown events"])
|
| 122 |
+
twist = random.choice(TWISTS)
|
| 123 |
+
bureaucratic = random.choice(BUREAUCRATIC_NOTES)
|
| 124 |
+
|
| 125 |
+
template = random.choice(TEMPLATES[status])
|
| 126 |
+
narrative = template.format(
|
| 127 |
+
name=name, location=location, action=action, adjective=adjective,
|
| 128 |
+
cause=cause, twist=twist, bureaucratic=bureaucratic
|
| 129 |
+
)
|
| 130 |
+
|
| 131 |
+
profile = {
|
| 132 |
+
"name": name,
|
| 133 |
+
"status": status,
|
| 134 |
+
"mood": mood,
|
| 135 |
+
"generated_at": datetime.utcnow().isoformat() + "Z",
|
| 136 |
+
"narrative": narrative,
|
| 137 |
+
"fictional_confidence": f"{random.randint(75,99)}% (fictional)"
|
| 138 |
+
}
|
| 139 |
+
|
| 140 |
+
return profile
|
| 141 |
+
|
| 142 |
+
def display_profile(profile):
|
| 143 |
+
st.markdown("---")
|
| 144 |
+
st.subheader(f"Profile: {profile['name']}")
|
| 145 |
+
st.write(f"**Generated at (UTC):** {profile['generated_at']}")
|
| 146 |
+
st.write(f"**Status:** {profile['status']}")
|
| 147 |
+
st.write(f"**Mood:** {profile['mood']}")
|
| 148 |
+
st.write(f"**Narrative:** {profile['narrative']}")
|
| 149 |
+
st.write(f"**Fictional confidence:** {profile['fictional_confidence']}")
|
| 150 |
+
st.markdown("---")
|
| 151 |
|
| 152 |
+
# Generate profile
|
| 153 |
+
if submitted:
|
| 154 |
+
profile = generate_profile(name, mood, seed_input, confirm_fictional)
|
| 155 |
+
if "error" in profile:
|
| 156 |
+
st.error(profile["error"])
|
| 157 |
+
else:
|
| 158 |
+
display_profile(profile)
|
| 159 |
+
st.json(profile)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|