Skip to content

Commit 18e7af7

Browse files
Yinch-panclaude
andcommitted
feat: add admin for bounties & workshop, seed demo data, move related articles to sidebar
- Add backend/apps/bounties/admin.py: register Bounty, BountyApplication, BountyDeliverable, BountyComment, BountyReview, Arbitration - Add backend/apps/workshop/admin.py: register Article, Series, Comment, Vote, Tip with publish/archive/feature actions - Add manage.py seed command: seeds 5 skills, 5 bounties, 5 articles (idempotent, --clear flag) - Move related articles section from main content to sidebar Card in ArticleDetailPage Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 23ce801 commit 18e7af7

4 files changed

Lines changed: 552 additions & 17 deletions

File tree

Lines changed: 382 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,382 @@
1+
"""Seed 5 demo items per module: Skills, Bounties, Workshop articles."""
2+
from django.core.management.base import BaseCommand
3+
from django.db import transaction
4+
from django.utils import timezone
5+
from django.utils.text import slugify
6+
7+
from apps.accounts.models import User
8+
from apps.bounties.models import Bounty, BountyType, BountyStatus, WorkloadEstimate
9+
from apps.skills.models import Skill, SkillCategory, SkillStatus, PricingModel
10+
from apps.workshop.models import Article, ArticleDifficulty, ArticleStatus, ArticleType
11+
12+
SKILLS = [
13+
{
14+
"name": "Python Code Reviewer",
15+
"description": "Automatically reviews Python code for style, bugs, and security issues using static analysis.",
16+
"category": SkillCategory.CODE_DEV,
17+
"pricing_model": PricingModel.FREE,
18+
"tags": ["python", "code-review", "static-analysis"],
19+
},
20+
{
21+
"name": "SEO Article Writer",
22+
"description": "Generates SEO-optimized articles with keyword density analysis and meta description suggestions.",
23+
"category": SkillCategory.WRITING,
24+
"pricing_model": PricingModel.PAID,
25+
"price": "0.50",
26+
"tags": ["seo", "writing", "content"],
27+
},
28+
{
29+
"name": "CSV Data Analyzer",
30+
"description": "Analyzes CSV datasets and produces summary statistics, charts, and anomaly detection reports.",
31+
"category": SkillCategory.DATA_ANALYTICS,
32+
"pricing_model": PricingModel.PAID,
33+
"price": "0.30",
34+
"tags": ["csv", "data", "analytics"],
35+
},
36+
{
37+
"name": "Academic Paper Summarizer",
38+
"description": "Summarizes academic papers into structured abstracts with key findings and methodology.",
39+
"category": SkillCategory.ACADEMIC,
40+
"pricing_model": PricingModel.FREE,
41+
"tags": ["academic", "summarization", "research"],
42+
},
43+
{
44+
"name": "Productivity Task Planner",
45+
"description": "Breaks down complex goals into actionable daily tasks with time estimates and priorities.",
46+
"category": SkillCategory.PRODUCTIVITY,
47+
"pricing_model": PricingModel.FREE,
48+
"tags": ["productivity", "planning", "gtd"],
49+
},
50+
]
51+
52+
BOUNTIES = [
53+
{
54+
"title": "Build a Markdown to PDF converter Skill",
55+
"description": "Need a Skill that converts Markdown documents to well-formatted PDFs with custom styling support. Must handle tables, code blocks, and images.",
56+
"bounty_type": BountyType.SKILL_CUSTOM,
57+
"reward": "15.00",
58+
"workload_estimate": WorkloadEstimate.TWO_TO_THREE_DAYS,
59+
"skill_requirements": "Python, markdown parsing, PDF generation (reportlab or weasyprint)",
60+
},
61+
{
62+
"title": "Translate 50 product descriptions EN→ZH",
63+
"description": "Translate 50 e-commerce product descriptions from English to Simplified Chinese. Maintain tone and marketing language.",
64+
"bounty_type": BountyType.CONTENT_CREATION,
65+
"reward": "8.00",
66+
"workload_estimate": WorkloadEstimate.ONE_DAY,
67+
"skill_requirements": "Native-level Chinese, e-commerce experience preferred",
68+
},
69+
{
70+
"title": "Fix pagination bug in Django Ninja API",
71+
"description": "Cursor-based pagination returns duplicate items when records are inserted between pages. Reproduce, diagnose, and fix with tests.",
72+
"bounty_type": BountyType.BUG_FIX,
73+
"reward": "5.00",
74+
"workload_estimate": WorkloadEstimate.ONE_TO_TWO_HOURS,
75+
"skill_requirements": "Django, Django Ninja, PostgreSQL",
76+
},
77+
{
78+
"title": "Scrape and structure AI tool directory",
79+
"description": "Scrape a public AI tools directory and output structured JSON with name, category, pricing, and description for 200+ tools.",
80+
"bounty_type": BountyType.DATA_PROCESSING,
81+
"reward": "12.00",
82+
"workload_estimate": WorkloadEstimate.HALF_DAY,
83+
"skill_requirements": "Python, BeautifulSoup or Playwright, JSON",
84+
},
85+
{
86+
"title": "Write onboarding email sequence (5 emails)",
87+
"description": "Write a 5-email onboarding sequence for a SaaS product targeting AI developers. Tone: friendly, technical, action-oriented.",
88+
"bounty_type": BountyType.GENERAL,
89+
"reward": "6.00",
90+
"workload_estimate": WorkloadEstimate.ONE_DAY,
91+
"skill_requirements": "Copywriting, SaaS marketing, email best practices",
92+
},
93+
]
94+
95+
_ARTICLE_CONTENT_1 = (
96+
"Prompt engineering is the practice of crafting inputs to AI models to "
97+
"get reliable, high-quality outputs. This guide covers the fundamentals "
98+
"you need to start building effective prompts.\n\n"
99+
"## Why Prompt Engineering Matters\n\n"
100+
"The same model can produce wildly different results depending on how you "
101+
"phrase your request. A well-engineered prompt can mean the difference "
102+
"between a generic response and a precisely targeted answer.\n\n"
103+
"## Core Techniques\n\n"
104+
"**1. Be Specific About Format**\n"
105+
'Instead of "summarize this", try "summarize this in 3 bullet points, '
106+
'each under 20 words, focusing on actionable takeaways."\n\n'
107+
"**2. Provide Context**\n"
108+
"Models perform better when they understand the audience and purpose. "
109+
'"Explain this to a senior Python developer" yields different results '
110+
'than "explain this to a beginner."\n\n'
111+
"**3. Use Examples (Few-Shot)**\n"
112+
"Showing the model 2-3 examples of the input/output pattern you want "
113+
"dramatically improves consistency.\n\n"
114+
"**4. Chain of Thought**\n"
115+
'For complex reasoning tasks, ask the model to "think step by step" '
116+
"before giving its final answer.\n\n"
117+
"## Common Pitfalls\n\n"
118+
"- Ambiguous instructions lead to inconsistent outputs\n"
119+
"- Overly long prompts can cause the model to lose focus\n"
120+
"- Not specifying output format forces post-processing\n\n"
121+
"## Next Steps\n\n"
122+
"Practice by iterating on a single prompt 10 times, changing one variable "
123+
"at a time. Track what works."
124+
)
125+
126+
_ARTICLE_CONTENT_2 = (
127+
"I spent three months manually summarizing research papers for my "
128+
"literature review. Then I built a pipeline with the Claude API that cut "
129+
"that time by 80%. Here's exactly what I did.\n\n"
130+
"## The Problem\n\n"
131+
"My workflow: download PDF, read abstract, skim methods, note key "
132+
"findings, add to Notion. For 200 papers, this took roughly 40 hours.\n\n"
133+
"## The Solution Architecture\n\n"
134+
"PDF -> text extraction (pdfplumber) -> Claude API -> structured JSON -> "
135+
"Notion API\n\n"
136+
"## Key Prompt Design\n\n"
137+
"The critical insight was asking Claude to output structured JSON rather "
138+
"than prose. I asked for fields like main_claim (one sentence), "
139+
"methodology (list), key_findings (3-5 items), limitations, and a "
140+
"relevance_score from 1-5.\n\n"
141+
"## Results\n\n"
142+
"- Processing time per paper: 45 seconds (was 12 minutes)\n"
143+
"- Accuracy vs manual review: ~85% on key findings\n"
144+
"- Total cost for 200 papers: ~$4.20\n\n"
145+
"## What Didn't Work\n\n"
146+
"First I tried asking for prose summaries. They were good but hard to "
147+
"compare across papers. Structured output was the unlock.\n\n"
148+
"## Code\n\n"
149+
'Full code is available as a Skill on this platform. Search "Research '
150+
'Paper Analyzer".'
151+
)
152+
153+
_ARTICLE_CONTENT_3 = (
154+
"Everyone celebrates longer context windows as pure upside. After running "
155+
"production workloads at scale, I've found the reality is more nuanced.\n\n"
156+
"## What the Marketing Says\n\n"
157+
'"1M token context! Fit your entire codebase!" This is technically true '
158+
"and genuinely useful for some tasks.\n\n"
159+
"## What Actually Happens at Scale\n\n"
160+
"**Latency increases non-linearly.** A 100K token prompt doesn't take "
161+
"10x longer than a 10K prompt — it can take 30-50x longer depending on "
162+
"the model and infrastructure.\n\n"
163+
"**Cost scales with input tokens.** If you're stuffing 500K tokens of "
164+
'context for every query, your costs explode even if the model is "cheap '
165+
'per token."\n\n'
166+
"**Quality degrades in the middle.** Research consistently shows models "
167+
"pay less attention to content in the middle of very long contexts (the "
168+
'"lost in the middle" problem).\n\n'
169+
"## When Long Context Is Worth It\n\n"
170+
"- One-shot analysis tasks where you need the full document\n"
171+
"- Tasks where retrieval errors are more costly than latency\n"
172+
"- Offline batch processing where latency doesn't matter\n\n"
173+
"## Better Alternatives for Most Cases\n\n"
174+
"1. **RAG (Retrieval Augmented Generation)**: Retrieve only relevant chunks\n"
175+
"2. **Hierarchical summarization**: Summarize sections, then summarize summaries\n"
176+
"3. **Structured extraction**: Pull out only the fields you need first\n\n"
177+
"## The Rule I Use\n\n"
178+
"If the task can be done with <20K tokens 90% of the time, build for that "
179+
"case and handle edge cases separately. Don't architect for the worst case."
180+
)
181+
182+
_ARTICLE_CONTENT_4 = (
183+
"I ran both models through 50 real coding tasks from my work over the "
184+
"past month. Here's what I found.\n\n"
185+
"## Test Methodology\n\n"
186+
"Tasks were drawn from actual work: bug fixes, feature implementations, "
187+
"refactoring, and code review. Each task was run on both models with "
188+
"identical prompts. I evaluated on: correctness (does it run?), quality "
189+
"(would I merge it?), and speed to usable output.\n\n"
190+
"## Results Summary\n\n"
191+
"| Category | Claude 3.5 Sonnet | GPT-4o |\n"
192+
"|----------|-------------------|--------|\n"
193+
"| Correctness | 88% | 84% |\n"
194+
"| Code quality | 4.2/5 | 3.9/5 |\n"
195+
"| Follows instructions | 4.5/5 | 4.1/5 |\n"
196+
"| Explains reasoning | 4.6/5 | 3.8/5 |\n\n"
197+
"## Where Claude Wins\n\n"
198+
"**Long file refactoring**: Claude maintains context better across 500+ "
199+
"line files and makes more consistent changes throughout.\n\n"
200+
"**Following constraints**: When I say \"don't use external libraries\" or "
201+
"\"keep the existing API surface\", Claude respects this more reliably.\n\n"
202+
"**Code explanation**: Claude's explanations of what it changed and why "
203+
"are significantly more useful for review.\n\n"
204+
"## Where GPT-4 Wins\n\n"
205+
"**Speed**: GPT-4o is noticeably faster for short tasks.\n\n"
206+
"**Familiarity with obscure libraries**: For niche packages with less "
207+
"training data, GPT-4 sometimes has better coverage.\n\n"
208+
"## My Current Setup\n\n"
209+
"I use Claude for anything requiring careful instruction-following or long "
210+
"context. GPT-4o for quick one-liners where speed matters."
211+
)
212+
213+
_ARTICLE_CONTENT_5 = (
214+
"As the CaMeL marketplace grows, we're seeing an interesting tension: "
215+
"users want stability (lock to a version that works), but also want "
216+
"improvements (auto-update to latest). How should we think about this?\n\n"
217+
"## The Problem\n\n"
218+
"Imagine you've built a workflow that depends on a Skill. The Skill "
219+
"author releases v2.0 with breaking prompt changes. Your workflow breaks "
220+
"silently.\n\n"
221+
"This is the npm left-pad problem, but for AI behavior.\n\n"
222+
"## Option A: Semantic Versioning (Current Approach)\n\n"
223+
"Skills use semver. Major version bumps signal breaking changes. Users "
224+
"can pin to ^1.0.0 or lock to 1.2.3.\n\n"
225+
"**Pros**: Familiar to developers, explicit contract\n"
226+
"**Cons**: AI behavior changes are fuzzy — is a 10% quality improvement "
227+
"a patch or minor?\n\n"
228+
"## Option B: Behavioral Snapshots\n\n"
229+
"Instead of versioning the prompt, version the *behavior* by running a "
230+
"test suite. A new version only ships if it passes all existing "
231+
"behavioral tests.\n\n"
232+
"**Pros**: Guarantees backward compatibility\n"
233+
"**Cons**: Hard to define \"behavioral tests\" for open-ended tasks\n\n"
234+
"## Option C: Immutable Versions + Deprecation\n\n"
235+
"Every published version is immutable forever. Authors can deprecate old "
236+
"versions but never delete them.\n\n"
237+
"**Pros**: Maximum stability\n"
238+
"**Cons**: Storage costs, users stuck on bad versions\n\n"
239+
"## What Do You Think?\n\n"
240+
"I'm genuinely uncertain which approach is right. The tradeoffs depend "
241+
"heavily on use case. What's your experience with Skill versioning so far?"
242+
)
243+
244+
ARTICLES = [
245+
{
246+
"title": "Getting Started with Prompt Engineering: A Practical Guide",
247+
"content": _ARTICLE_CONTENT_1,
248+
"difficulty": ArticleDifficulty.BEGINNER,
249+
"article_type": ArticleType.TUTORIAL,
250+
"model_tags": ["claude-3", "gpt-4"],
251+
"custom_tags": ["prompt-engineering", "beginner"],
252+
},
253+
{
254+
"title": "How I Automated My Research Workflow with Claude API",
255+
"content": _ARTICLE_CONTENT_2,
256+
"difficulty": ArticleDifficulty.INTERMEDIATE,
257+
"article_type": ArticleType.CASE_STUDY,
258+
"model_tags": ["claude-3-5-sonnet"],
259+
"custom_tags": ["automation", "research", "api"],
260+
},
261+
{
262+
"title": "The Hidden Cost of Long Context Windows",
263+
"content": _ARTICLE_CONTENT_3,
264+
"difficulty": ArticleDifficulty.INTERMEDIATE,
265+
"article_type": ArticleType.PITFALL,
266+
"model_tags": ["claude-3", "gpt-4"],
267+
"custom_tags": ["context-window", "performance", "cost"],
268+
},
269+
{
270+
"title": "Claude vs GPT-4 for Code Generation: A Practical Comparison",
271+
"content": _ARTICLE_CONTENT_4,
272+
"difficulty": ArticleDifficulty.INTERMEDIATE,
273+
"article_type": ArticleType.REVIEW,
274+
"model_tags": ["claude-3-5-sonnet", "gpt-4o"],
275+
"custom_tags": ["comparison", "code-generation"],
276+
},
277+
{
278+
"title": "Should AI Skills Have Versioning? A Community Discussion",
279+
"content": _ARTICLE_CONTENT_5,
280+
"difficulty": ArticleDifficulty.BEGINNER,
281+
"article_type": ArticleType.DISCUSSION,
282+
"model_tags": [],
283+
"custom_tags": ["versioning", "marketplace", "community"],
284+
},
285+
]
286+
287+
288+
class Command(BaseCommand):
289+
help = "Seed 5 demo items per module (Skills, Bounties, Workshop articles)."
290+
291+
def add_arguments(self, parser):
292+
parser.add_argument(
293+
"--clear",
294+
action="store_true",
295+
help="Delete existing seed data before re-seeding (matches usernames seed_user_*).",
296+
)
297+
298+
def handle(self, *args, **options):
299+
with transaction.atomic():
300+
if options["clear"]:
301+
User.objects.filter(username__startswith="seed_user_").delete()
302+
self.stdout.write("Cleared existing seed data.")
303+
304+
user = self._get_or_create_seed_user()
305+
self._seed_skills(user)
306+
self._seed_bounties(user)
307+
self._seed_articles(user)
308+
309+
self.stdout.write(self.style.SUCCESS("Seeded 5 skills, 5 bounties, 5 articles."))
310+
311+
def _get_or_create_seed_user(self):
312+
user, created = User.objects.get_or_create(
313+
username="seed_user_demo",
314+
defaults={
315+
"email": "demo@camel.community",
316+
"display_name": "Demo User",
317+
"credit_score": 500,
318+
"balance": "100.00",
319+
},
320+
)
321+
if created:
322+
user.set_password("demo_password_123")
323+
user.save(update_fields=["password"])
324+
return user
325+
326+
def _seed_skills(self, user):
327+
for data in SKILLS:
328+
slug = slugify(data["name"])
329+
skill, created = Skill.objects.get_or_create(
330+
slug=slug,
331+
defaults={
332+
"creator": user,
333+
"name": data["name"],
334+
"description": data["description"],
335+
"category": data["category"],
336+
"pricing_model": data["pricing_model"],
337+
"price": data.get("price"),
338+
"tags": data["tags"],
339+
"status": SkillStatus.APPROVED,
340+
},
341+
)
342+
if created:
343+
self.stdout.write(f" Skill: {skill.name}")
344+
345+
def _seed_bounties(self, user):
346+
deadline = timezone.now() + timezone.timedelta(days=14)
347+
for data in BOUNTIES:
348+
bounty, created = Bounty.objects.get_or_create(
349+
title=data["title"],
350+
creator=user,
351+
defaults={
352+
"description": data["description"],
353+
"bounty_type": data["bounty_type"],
354+
"reward": data["reward"],
355+
"workload_estimate": data["workload_estimate"],
356+
"skill_requirements": data["skill_requirements"],
357+
"status": BountyStatus.OPEN,
358+
"deadline": deadline,
359+
},
360+
)
361+
if created:
362+
self.stdout.write(f" Bounty: {bounty.title}")
363+
364+
def _seed_articles(self, user):
365+
for data in ARTICLES:
366+
slug = slugify(data["title"])
367+
article, created = Article.objects.get_or_create(
368+
slug=slug,
369+
defaults={
370+
"author": user,
371+
"title": data["title"],
372+
"content": data["content"],
373+
"difficulty": data["difficulty"],
374+
"article_type": data["article_type"],
375+
"model_tags": data["model_tags"],
376+
"custom_tags": data["custom_tags"],
377+
"status": ArticleStatus.PUBLISHED,
378+
"published_at": timezone.now(),
379+
},
380+
)
381+
if created:
382+
self.stdout.write(f" Article: {article.title}")

0 commit comments

Comments
 (0)