From 66440c6b7513bdc353aafc23f020e6f1f330b684 Mon Sep 17 00:00:00 2001 From: stabldev <114811070+stabldev@users.noreply.github.com> Date: Fri, 20 Mar 2026 21:42:58 +0530 Subject: [PATCH] chore: make seed db logic realistic --- backend/quiblet/management/commands/seed.py | 653 +++++++++++++------- 1 file changed, 419 insertions(+), 234 deletions(-) diff --git a/backend/quiblet/management/commands/seed.py b/backend/quiblet/management/commands/seed.py index 541f0d91..e97c7b65 100644 --- a/backend/quiblet/management/commands/seed.py +++ b/backend/quiblet/management/commands/seed.py @@ -1,40 +1,399 @@ -from django.core.management.base import BaseCommand import uuid import random +from typing import Any + +from django.core.management.base import BaseCommand + from quiblet.models import Quiblet, Quib, Comment, CommentVote, QuibVote +# ────────────────────────────────────────────── +# Hardcoded user pool – these must already exist +# in the auth service; the seed only references +# their UUIDs. +# ────────────────────────────────────────────── +USERS = { + "liamtech": uuid.UUID("e9c8d605-b61e-48cb-afd5-60b141b71ec6"), + "noahbuilds": uuid.UUID("1a927bd4-f986-49d4-a036-9f6f690c5070"), + "mayacodes": uuid.UUID("20c8006f-af50-4551-a1e7-9db8f10121e2"), + "sofiascripts": uuid.UUID("bb74af32-2bbe-421e-8cfe-4f94e8f124a5"), + "alexdev": uuid.UUID("e9c8d605-b61e-48cb-afd5-60b141b71ec6"), +} + +USER_IDS = list(USERS.values()) + + class Command(BaseCommand): help = "Seeds the database with realistic quiblets, quibs, and comments." + # ────────────────────────────────────────── + # Quiblets + # ────────────────────────────────────────── + QUIBLETS_DATA = [ + { + "name": "programming", + "description": "Everything about code, frameworks, and architecture.", + "title": "Programming & CS", + }, + { + "name": "gaming", + "description": "Discuss your favorite games, consoles, and indie gems.", + "title": "The Gaming Hub", + }, + { + "name": "philosophy", + "description": "Deep thoughts, ethics, and existential questions.", + "title": "Philosophy Corner", + }, + ] + + # ────────────────────────────────────────── + # Quibs (posts) + # Each entry has a quiblet index, the poster + # username, HTML content, and per-post + # comments with realistic replies. + # ────────────────────────────────────────── + QUIBS_DATA: list[dict[str, Any]] = [ + # ── Programming ────────────────────── + { + "quiblet_idx": 0, + "poster": "liamtech", + "title": "Why Rust is the future of systems programming", + "content": ( + "
Rust has been the most loved language on the Stack Overflow survey for years, and for good reason. " + "It solves the age-old problem of memory safety without the overhead of a garbage collector.
" + "Is it time to start rewriting our C++ legacy codebases? Let's discuss.
" + ), + "comments": [ + { + "commenter": "noahbuilds", + "content": "I've been migrating a networking daemon from C to Rust over the past month and the borrow checker alone has caught at least four bugs that our old test suite missed. The learning curve is real but absolutely worth it.", + "reply": { + "commenter": "liamtech", + "content": "That's awesome to hear. Did you use any FFI to bridge the old C code during the migration, or did you go for a full rewrite?", + }, + }, + { + "commenter": "mayacodes", + "content": "Honest question — for a small REST API does Rust really make sense over something like Go? The compile times alone feel like a productivity hit for rapid iteration.", + }, + { + "commenter": "sofiascripts", + "content": "The ecosystem is getting better every month. Between axum, tokio, and serde the web story is almost there. Still wish the async trait story was smoother though.", + }, + ], + }, + { + "quiblet_idx": 0, + "poster": "mayacodes", + "title": "Must-have VS Code extensions for 2026", + "content": ( + "If you aren't using these extensions, are you even coding?
" + "Pro tip: Use a custom font like Fira Code for those sweet ligatures!
" + ), + "comments": [ + { + "commenter": "noahbuilds", + "content": "Error Lens is a game-changer. I used to waste so much time hovering over red squigglies. One extension I'd add is **Thunder Client** — it's basically Postman inside VS Code.", + "reply": { + "commenter": "mayacodes", + "content": "Oh I tried Thunder Client last week! It's surprisingly polished. I might drop Postman entirely.", + }, + }, + { + "commenter": "sofiascripts", + "content": "Peacock is underrated. When you have three VS Code windows open for a monorepo it saves your sanity.", + }, + { + "commenter": "liamtech", + "content": "No love for Vim keybindings? Once you go modal you never go back.", + }, + ], + }, + { + "quiblet_idx": 0, + "poster": "noahbuilds", + "title": "Async/Await: It's not as scary as it looks", + "content": ( + "Async programming is often seen as a dark art, but once you understand the Event Loop, everything clicks.
" + "\"The event loop is like a waiter. It takes your order, tells the kitchen, and moves to the next table instead of waiting by the stove.\"
" + "The key insight is that await doesn't block the thread — it yields control back to the loop so other tasks can run in the meantime.
" + "Once that mental model clicks, writing concurrent I/O-bound code becomes almost intuitive.
" + ), + "comments": [ + { + "commenter": "liamtech", + "content": "The waiter analogy is the best one I've seen. I usually explain it with a kitchen metaphor too but yours is cleaner.", + "reply": { + "commenter": "noahbuilds", + "content": "Thanks! I stole it from a PyCon talk years ago and it's stuck with me ever since.", + }, + }, + { + "commenter": "mayacodes", + "content": "I still trip up on the difference between concurrency and parallelism. Async gives you concurrency on a single thread, right? You need multiprocessing for true parallelism?", + }, + { + "commenter": "sofiascripts", + "content": "For anyone reading this, be careful with async in Django — most of the ORM is still sync under the hood. You need `sync_to_async` or channels for real async views.", + }, + ], + }, + { + "quiblet_idx": 0, + "poster": "sofiascripts", + "title": "The rise of HTMX: Are SPAs dying?", + "content": ( + "HTMX is taking the world by storm. It allows you to access AJAX, CSS transitions, WebSockets and Server-Sent Events directly in HTML using attributes.
" + "Is the industry swinging back to server-side rendering? I think so, and HTMX is leading the charge.
" + ), + "comments": [ + { + "commenter": "liamtech", + "content": "I used HTMX on a side project last month and it was refreshing. But for anything with complex client-side state — drag-and-drop, real-time collab — I'd still reach for React or Svelte.", + "reply": { + "commenter": "sofiascripts", + "content": "Totally fair. HTMX shines on content-heavy CRUD apps. For rich interactivity you still need a proper framework.", + }, + }, + { + "commenter": "noahbuilds", + "content": "SPAs aren't dying, they're just being used where they actually make sense. The industry over-applied them and now there's a correction.", + }, + { + "commenter": "mayacodes", + "content": "The biggest win for me is that HTMX works beautifully with Django templates. No separate API layer, no serializers for simple pages. Just render HTML on the server and swap it in.", + }, + ], + }, + # ── Gaming ─────────────────────────── + { + "quiblet_idx": 1, + "poster": "noahbuilds", + "title": "Elden Ring: Shadow of the Erdtree impressions", + "content": ( + "I've spent 20 hours in the DLC and I'm still blown away. FromSoftware has done it again. " + "The verticality of the map is insane.
" + "Has anyone found the secret path to the Abyssal Woods?
" + ), + "comments": [ + { + "commenter": "liamtech", + "content": "The Abyssal Woods entrance is through the coffin ride after the Putrescent Knight fight in the Stone Coffin Fissure. Easy to miss if you don't explore that area thoroughly.", + "reply": { + "commenter": "noahbuilds", + "content": "Wait WHAT. I ran past that coffin like three times. Heading back in tonight, thanks!", + }, + }, + { + "commenter": "sofiascripts", + "content": "25 hours in and I'm still in the first major area. I keep getting distracted exploring every ruin and catacomb. This DLC is massive.", + }, + { + "commenter": "mayacodes", + "content": "The Backhand Blades are the most fun I've had with a weapon in any Souls game. The moveset feels like you're playing a character action game.", + }, + ], + }, + { + "quiblet_idx": 1, + "poster": "mayacodes", + "title": "Indie games are carrying the industry right now", + "content": ( + "While AAA studios are playing it safe with sequels and microtransactions, indie devs are innovating.
" + "Support your local indie dev!
" + ), + "comments": [ + { + "commenter": "noahbuilds", + "content": "Balatro consumed my entire weekend. I told myself \"one more run\" about fourteen times. The way jokers synergize is just *chef's kiss*.", + "reply": { + "commenter": "mayacodes", + "content": "The Hologram + Blueprint combo is absurd. I had a run where every hand was scoring 10 million chips by ante 6.", + }, + }, + { + "commenter": "liamtech", + "content": "Don't sleep on **Caves of Qud** either. It's technically been in early access forever but the 1.0 release is shaping up to be one of the deepest RPGs ever made.", + }, + { + "commenter": "sofiascripts", + "content": "Animal Well blew my mind. No hand-holding, no quest markers, just pure exploration. I spent an hour staring at a wall before realizing there was a hidden passage.", + }, + ], + }, + { + "quiblet_idx": 1, + "poster": "liamtech", + "title": "Is cloud gaming finally viable?", + "content": ( + "I tried GeForce Now on a 100 Mbps fiber connection and honestly — I couldn't tell the difference from local hardware.
" + "Are you ready to sell your GPU?
" + ), + "comments": [ + { + "commenter": "noahbuilds", + "content": "Tried it for single-player games and it's great. But for competitive shooters the extra 20-30 ms of latency is noticeable. I'll keep my GPU for now.", + "reply": { + "commenter": "liamtech", + "content": "Yeah that's fair. For competitive stuff every millisecond counts. For my Stardew Valley addiction though? Cloud is perfect.", + }, + }, + { + "commenter": "sofiascripts", + "content": "The ownership question is the dealbreaker for me. I like knowing my library is mine. Steam's offline mode means I can play even if the internet goes down.", + }, + { + "commenter": "mayacodes", + "content": "I live in a rural area with 20 Mbps DSL. Cloud gaming is a non-starter for me. Until infrastructure catches up everywhere, it's still a luxury.", + }, + ], + }, + # ── Philosophy ─────────────────────── + { + "quiblet_idx": 2, + "poster": "sofiascripts", + "title": "The Trolley Problem in the age of Autonomous Vehicles", + "content": ( + "The classic ethical dilemma is no longer just for textbooks. As we program self-driving cars, we are essentially codifying morality.
" + "We are the architects of silicon ethics.
" + ), + "comments": [ + { + "commenter": "noahbuilds", + "content": "The uncomfortable truth is that these cars will statistically save far more lives than they take, even with imperfect ethics. The real trolley problem is choosing *not* to deploy them.", + "reply": { + "commenter": "sofiascripts", + "content": "That's a compelling utilitarian argument. But the families of the statistical few who *are* harmed won't see it that way. There's a difference between a human error and a design decision.", + }, + }, + { + "commenter": "liamtech", + "content": "From a legal standpoint this is a nightmare. If the algorithm chooses to swerve into a wall to avoid pedestrians, who's liable — the car owner, the manufacturer, or the ML engineer who trained the model?", + }, + { + "commenter": "mayacodes", + "content": "I think the trolley framing itself is misleading. Real self-driving systems don't make binary choices — they're constantly optimizing trajectories. The ethical question is more about how we define the optimization function.", + }, + ], + }, + { + "quiblet_idx": 2, + "poster": "liamtech", + "title": "Stoicism for the Modern Developer", + "content": ( + "\"You have power over your mind — not outside events. Realize this, and you will find strength.\" — Marcus Aurelius
" + "Next time a production server goes down at 2 AM, try a stoic breath before reaching for the keyboard.
" + ), + "comments": [ + { + "commenter": "mayacodes", + "content": "I started reading Meditations last year and it genuinely changed how I handle on-call rotations. Instead of panicking when a PagerDuty alert fires, I take a breath and triage calmly. Sounds simple but it works.", + "reply": { + "commenter": "liamtech", + "content": "Exactly. The Stoics weren't about suppressing emotions — they were about not letting emotions drive your decisions. Triage first, feel later.", + }, + }, + { + "commenter": "noahbuilds", + "content": "There's also the Stoic concept of *amor fati* — love of fate. That legacy codebase you inherited? It's your opportunity to practice patience and craftsmanship. Reframe the obstacle as the way.", + }, + { + "commenter": "sofiascripts", + "content": "Pairing this with deep work practices has been incredible for my productivity. Block distractions, accept what you can't control, and focus entirely on the task at hand.", + }, + ], + }, + { + "quiblet_idx": 2, + "poster": "noahbuilds", + "title": "What is Quibble? A philosophical take", + "content": ( + "Is Quibble just another social platform? Or is it a digital manifestation of the agora — the ancient public forum?
" + "In a world of short-form content and vanishing thoughts, we need a place for deep conversation. " + "A place where the discussion survives the scroll.
" + "What does it mean to truly be a member of a digital community? Is it lurking? Posting? Or something deeper?
" + ), + "comments": [ + { + "commenter": "sofiascripts", + "content": "I think the answer is somewhere between posting and listening. A good community member doesn't just broadcast — they engage thoughtfully with what others share. Quibble's threading model encourages that.", + "reply": { + "commenter": "noahbuilds", + "content": "Well said. The threaded comments are the key differentiator. It's not just a feed — it's a conversation tree. Ideas can branch and evolve.", + }, + }, + { + "commenter": "liamtech", + "content": "Every platform starts with idealism and ends with growth hacking. I hope Quibble stays focused on quality over engagement metrics. The moment you optimize for time-on-site, you lose the soul.", + }, + { + "commenter": "mayacodes", + "content": "The agora metaphor is perfect. In Athens the agora was both marketplace and debate hall. Quibble could be both — a place to share projects *and* argue about whether tabs or spaces matter.", + }, + ], + }, + ] + + # ────────────────────────────────────────── + # Main handler + # ────────────────────────────────────────── def handle(self, *args, **options): self.stdout.write("Seeding database...") - # 1. Create simulated users (UUIDs) - user_ids = [uuid.uuid4() for _ in range(5)] - self.stdout.write(f"Created {len(user_ids)} simulated user IDs.") - - # 2. Create Quiblets - quiblets_data = [ - { - "name": "programming", - "description": "Everything about code, frameworks, and architecture.", - "title": "Programming & CS", - }, - { - "name": "gaming", - "description": "Discuss your favorite games, consoles, and indie gems.", - "title": "The Gaming Hub", - }, - { - "name": "philosophy", - "description": "Deep thoughts, ethics, and existential questions.", - "title": "Philosophy Corner", - }, - ] - + # ── 1. Create Quiblets ──────────────── quiblets = [] - for data in quiblets_data: + for data in self.QUIBLETS_DATA: quiblet, created = Quiblet.objects.get_or_create( name=data["name"], defaults={ @@ -45,242 +404,68 @@ def handle(self, *args, **options): ) quiblets.append(quiblet) if created: - self.stdout.write( - self.style.SUCCESS(f"Created quiblet: q/{quiblet.name}") - ) - # Add a moderator and some members - mod_id = random.choice(user_ids) + self.stdout.write(self.style.SUCCESS(f"Created quiblet: q/{quiblet.name}")) + # First user becomes moderator, others join as members + mod_id = USERS["liamtech"] quiblet.members.create(member_id=mod_id, is_moderator=True) - for user_id in user_ids: - if user_id != mod_id and random.random() > 0.3: - quiblet.members.create(member_id=user_id) + for username, uid in USERS.items(): + if uid != mod_id: + quiblet.members.create(member_id=uid) else: self.stdout.write(f"Quiblet q/{quiblet.name} already exists.") - # 3. Create Quibs (10 quibs) - quibs_data = [ - # Programming - { - "quiblet": quiblets[0], - "title": "Why Rust is the future of systems programming", - "content": """ -# The Case for Rust - -Rust has been the "most loved" language for years, and for good reason. It solves the age-old problem of **memory safety** without the overhead of a garbage collector. - -### Key Benefits: -- **Fearless Concurrency**: No more data races. -- **Zero-cost Abstractions**: High-level feel, low-level performance. -- **Great Package Manager**: `cargo` is miles ahead of many others. - -```rust -fn main() { - println!("Hello, Web3 and Systems world!"); -} -``` - -Is it time to start rewriting our C++ legacy codebases? Let's discuss. -""", - }, - { - "quiblet": quiblets[0], - "title": "Must-have VS Code extensions for 2024", - "content": """ -If you aren't using these extensions, are you even coding? 😅 - -1. **Error Lens**: Highlights errors inline so you don't have to hover. -2. **Peacock**: Color-code your workspace to differentiate between windows. -3. **GitLens**: Supercharge your Git workflow. -4. **Prettier/ESLint**: Standardize your formatting. - -*Pro tip: Use a custom font like Fira Code for those sweet ligatures!* -""", - }, - { - "quiblet": quiblets[0], - "title": "Async/Await: It's not as scary as it looks", - "content": """ -Async programming is often seen as a dark art, but once you understand the **Event Loop**, everything clicks. - -> "The event loop is like a waiter. It takes your order, tells the kitchen, and moves to the next table instead of waiting by the stove." - -In Python, it looks like this: -```python -import asyncio - -async def main(): - print("Working...") - await asyncio.sleep(1) - print("Done!") - -asyncio.run(main()) -``` -""", - }, - { - "quiblet": quiblets[0], - "title": "The rise of HTMX: Are SPAs dying?", - "content": """ -HTMX is taking the world by storm. It allows you to access AJAX, CSS Transitions, WebSockets and Server Sent Events directly in HTML, using attributes. - -**Why it matters:** -- **Less Javascript**: You don't always need 2MB of React to toggle a dropdown. -- **Locality of Behavior**: Keep your logic where your HTML is. - -Is the industry swinging back to server-side rendering? -""", - }, - # Gaming - { - "quiblet": quiblets[1], - "title": "Elden Ring: Shadow of the Erdtree impressions", - "content": """ -# Return to the Lands Between - -I've spent 20 hours in the DLC and I'm still blown away. FromSoftware has done it again. The verticality of the map is **insane**. - -### The Good: -- New weapon types are incredible (Backhand blades!) -- The atmosphere is peak FromSoft. -- Bosses are challenging but fair (mostly). - -### The Bad: -- The performance on some consoles is still a bit jittery. - -*Has anyone found the secret path to the Abyssal Woods?* -""", - }, - { - "quiblet": quiblets[1], - "title": "Indie games are carrying the industry right now", - "content": """ -While AAA studios are playing it safe with sequels and microtransactions, indie devs are innovating. - -- **Balatro**: Who knew a poker roguelike could be this addictive? -- **Animal Well**: A masterclass in atmosphere and discovery. -- **Hades II**: Improving on perfection. - -Support your local indie dev! 🕹️ -""", - }, - { - "quiblet": quiblets[1], - "title": "Is cloud gaming finally viable?", - "content": """ -I tried **GeForce Now** on a 100Mbps fiber connection and... I couldn't tell the difference from local hardware. - -Points to consider: -* [ ] Latency (the biggest hurdle) -* [ ] Ownership (if the service dies, do you lose the game?) -* [ ] Subscription fatigue - -Are you ready to sell your GPU? -""", - }, - # Philosophy - { - "quiblet": quiblets[2], - "title": "The Trolley Problem in the age of Autonomous Vehicles", - "content": """ -The classic ethical dilemma is no longer just for textbooks. As we program self-driving cars, we are essentially codifying morality. - -1. Should the car protect the driver at all costs? -2. Should it minimize the total number of casualties? -3. What if the choice is between a dog and a person? - -**We are the architects of silicon ethics.** -""", - }, - { - "quiblet": quiblets[2], - "title": "Stoicism for the Modern Developer", - "content": """ -> "You have power over your mind — not outside events. Realize this, and you will find strength." — Marcus Aurelius - -Applied to coding: -- **Externals**: Stakeholder changes, legacy bugs, server outages. -- **Internals**: Your reaction, your code quality, your focus. - -Next time a production server goes down, try a stoic breath. -""", - }, - { - "quiblet": quiblets[2], - "title": "What is Quibble? A philosophical take", - "content": """ -Is Quibble just another social platform? Or is it a digital manifestation of the *Aura*? - -In a world of short-form content and vanishing thoughts, we need a place for **deep quibs**. A place where the conversation survives the scroll. - -What does it mean to be a member of a digital community? -""", - }, - ] - + # ── 2. Create Quibs ─────────────────── created_quibs = [] - for i, data in enumerate(quibs_data): - poster_id = random.choice(user_ids) + for data in self.QUIBS_DATA: + quiblet = quiblets[data["quiblet_idx"]] + poster_id = USERS[data["poster"]] + quib = Quib.objects.create( - quiblet=data["quiblet"], + quiblet=quiblet, poster_id=poster_id, title=data["title"], content=data["content"], status=Quib.Status.APPROVED, is_published=True, ) - created_quibs.append(quib) - self.stdout.write(f"Created quib: {quib.title} ({quib.id})") + created_quibs.append((quib, data)) + self.stdout.write(f" Created quib: \"{quib.title}\" by {data['poster']}") - # 4. Create Comments - comments_pool = [ - "Great post! I totally agree with this.", - "I have to disagree here. Have you considered the alternatives?", - "Interesting perspective. Thanks for sharing!", - "Can you provide more details on this part?", - "This really helped me understand the topic better.", - "I'm not sure about this one, seems a bit controversial.", - "Exactly what I was looking for!", - "Nice one! Looking forward to more posts like this.", - ] + # ── 3. Create Comments & Replies ────── + for quib, data in created_quibs: + for c in data["comments"]: + commenter_id = USERS[c["commenter"]] - for quib in created_quibs: - num_comments = random.randint(2, 5) - for _ in range(num_comments): - commenter_id = random.choice(user_ids) - # Use create_child as seen in the views + # Top-level comment comment = Comment.objects.create_child( parent=None, quib=quib, commenter_id=commenter_id, - content=random.choice(comments_pool), - ) - - # Add a vote for the comment - CommentVote.objects.create( - comment=comment, voter_id=commenter_id, value=1 + content=c["content"], ) + # Auto-upvote own comment + CommentVote.objects.create(comment=comment, voter_id=commenter_id, value=1) - # Add a reply to some comments (nested) - if random.random() > 0.5: - reply_id = random.choice(user_ids) + # Reply (if present) + if "reply" in c: + r = c["reply"] + reply_id = USERS[r["commenter"]] reply = Comment.objects.create_child( parent=comment, quib=quib, commenter_id=reply_id, - content=f"Replying to {commenter_id}: " - + random.choice(comments_pool), - ) - CommentVote.objects.create( - comment=reply, voter_id=reply_id, value=1 + content=r["content"], ) + CommentVote.objects.create(comment=reply, voter_id=reply_id, value=1) - # Add some votes for the quib itself - for voter_id in user_ids: - if random.random() > 0.3: + # ── 4. Quib votes ───────────────── + # Simulate organic voting: each user has a chance to vote + for uid in set(USER_IDS): + if random.random() > 0.25: QuibVote.objects.create( quib=quib, - voter_id=voter_id, - value=random.choice([1, 1, 1, -1]), # Weight towards upvotes + voter_id=uid, + value=random.choice([1, 1, 1, -1]), # skew towards upvotes ) - self.stdout.write(self.style.SUCCESS("Database seeded successfully!")) + self.stdout.write(self.style.SUCCESS("\nDatabase seeded successfully!"))