> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/Metaculus/metaculus/llms.txt
> Use this file to discover all available pages before exploring further.

# Projects

> Organize questions into collections, communities, and categorized groups

## Overview

Projects are the organizational structure of Metaculus. They group related questions together and control access, visibility, and governance. Every question belongs to exactly one primary project and can be tagged to multiple additional projects.

## Project Types

Metaculus supports multiple project types for different use cases (from `projects/models.py:213-223`):

```python theme={null}
class ProjectTypes(models.TextChoices):
    SITE_MAIN = "site_main"              # Main Metaculus site
    INDEX = "index"                      # Aggregate indices (e.g., democracy index)
    TOURNAMENT = "tournament"            # Time-bound competitions with prizes
    QUESTION_SERIES = "question_series" # Ongoing question collections
    PERSONAL_PROJECT = "personal_project" # User-created collections
    NEWS_CATEGORY = "news_category"     # News and current events
    CATEGORY = "category"               # General topic categories
    LEADERBOARD_TAG = "leaderboard_tag" # Tags for leaderboard filtering
    TOPIC = "topic"                     # Subject matter tags
    COMMUNITY = "community"             # Community-run projects
```

<CardGroup cols={3}>
  <Card title="Tournaments" icon="medal">
    Competitions with prizes and leaderboards
  </Card>

  <Card title="Communities" icon="users">
    Independent forecasting communities
  </Card>

  <Card title="Categories" icon="tags">
    Organize by topic (AI, climate, politics)
  </Card>

  <Card title="Question Series" icon="list">
    Ongoing question collections on specific topics
  </Card>

  <Card title="Indices" icon="chart-line">
    Aggregate metrics from multiple questions
  </Card>

  <Card title="Personal Projects" icon="user">
    Custom question collections
  </Card>
</CardGroup>

## Project Structure

From `projects/models.py:212-402`, the core project model:

```python theme={null}
class Project(TimeStampedModel, TranslatedModel):
    type = models.CharField(max_length=32, choices=ProjectTypes.choices)
    
    # Identification
    name = models.CharField(max_length=200)
    slug = models.CharField(max_length=200, validators=[validate_alpha_slug])
    
    # Description and branding
    subtitle = models.CharField(max_length=255, blank=True, default="")
    description = models.TextField(blank=True, default="")
    header_image = models.ImageField(null=True, blank=True)
    header_logo = models.ImageField(null=True, blank=True)
    emoji = models.CharField(max_length=10, default="", blank=True)
    
    # Tournament/Competition fields
    prize_pool = models.DecimalField(decimal_places=2, max_digits=15, null=True)
    start_date = models.DateTimeField(null=True, blank=True)
    close_date = models.DateTimeField(null=True, blank=True)
    forecasting_end_date = models.DateTimeField(null=True, blank=True)
    
    # Leaderboards
    primary_leaderboard = models.ForeignKey("scoring.Leaderboard", null=True)
    
    # Permissions
    default_permission = models.CharField(choices=ObjectPermission.choices)
    override_permissions = models.ManyToManyField(User, through="ProjectUserPermission")
    created_by = models.ForeignKey(User, on_delete=models.CASCADE)
    
    # Visibility
    visibility = models.CharField(choices=Visibility.choices)
    show_on_homepage = models.BooleanField(default=False)
    
    # Metrics
    followers_count = models.PositiveIntegerField(default=0)
    forecasts_count = models.PositiveIntegerField(default=0)
    forecasters_count = models.PositiveIntegerField(default=0)
```

## Question-Project Relationship

Questions are associated with projects through posts:

### Primary Project

Every question belongs to exactly one primary project through its post (from `questions/models.py:249-259`):

```python theme={null}
post = models.ForeignKey(
    "posts.Post",
    on_delete=models.CASCADE,
    related_name="questions",
)

# Post has:
default_project = models.ForeignKey(Project, on_delete=models.CASCADE)
```

### Additional Projects (Tags)

Posts can be tagged to multiple additional projects:

```python theme={null}
projects = models.ManyToManyField(Project, related_name="posts")
```

<Info>
  The primary project (`default_project`) controls governance, while additional projects (`projects`) provide categorization and discovery.
</Info>

## Permissions

Projects use a hierarchical permission system to control access.

### Permission Levels

From `projects/permissions.py`, the available permissions are:

* **`null`**: Private project - no access
* **`VIEWER`**: Can view questions and forecasts
* **`FORECASTER`**: Can view and make predictions
* **`CURATOR`**: Can view, forecast, and help curate content
* **`ADMIN`**: Full control over project

### Default Permission

From `projects/models.py:332-339`:

```python theme={null}
default_permission = models.CharField(
    choices=ObjectPermission.choices,
    null=True,  # null -> private
    blank=True,
    default=ObjectPermission.FORECASTER,
    db_index=True,
)
```

<Warning>
  Setting `default_permission = null` makes the project **completely private** - only users with explicit permission overrides can access it.
</Warning>

### Permission Overrides

Individual users can have custom permissions (from `projects/models.py:498-513`):

```python theme={null}
class ProjectUserPermission(TimeStampedModel):
    user = models.ForeignKey(User, on_delete=models.CASCADE)
    project = models.ForeignKey(Project, on_delete=models.CASCADE)
    permission = models.CharField(choices=ObjectPermission.choices)
    
    class Meta:
        constraints = [
            models.UniqueConstraint(
                name="projectuserpermission_unique_user_id_project_id",
                fields=["user_id", "project_id"],
            ),
        ]
```

### Permission Resolution

From `projects/models.py:162-191`, permissions are resolved with priority:

1. **Superuser**: Automatically get `ADMIN` permission
2. **Creator**: Project creator gets `ADMIN` permission
3. **Override**: Explicit `ProjectUserPermission` if set
4. **Default**: Falls back to project's `default_permission`

```python theme={null}
def annotate_user_permission(self, user: User = None):
    if user and user.is_superuser:
        return self.annotate(user_permission=models.Value(ObjectPermission.ADMIN))
    
    return self.annotate(
        user_permission=models.Case(
            models.When(
                Q(created_by_id__isnull=False, created_by_id=user_id),
                then=models.Value(ObjectPermission.ADMIN),
            ),
            default=Coalesce(
                F("_user_permission_override__permission"),
                F("default_permission"),
            ),
        ),
    )
```

## Visibility

Projects control how they appear across the platform.

### Visibility Options

From `projects/models.py:229-362`:

```python theme={null}
class Visibility(models.TextChoices):
    NORMAL = "normal"
    NOT_IN_MAIN_FEED = "not_in_main_feed"
    UNLISTED = "unlisted"
```

<Tabs>
  <Tab title="Normal">
    * Visible on the main feed
    * Questions contribute to global leaderboards and medals
    * Listed on tournaments/question series page

    **Use for**: Public competitions and major question collections
  </Tab>

  <Tab title="Not In Main Feed">
    * Not visible in main feed but searchable
    * Does NOT contribute to global leaderboards/medals
    * Listed on tournaments/question series page

    **Use for**: Specialized topics that shouldn't clutter the main feed

    **Default for**: Most project types
  </Tab>

  <Tab title="Unlisted">
    * Not visible in main feed
    * Not searchable
    * Does NOT contribute to global leaderboards/medals
    * Not listed on tournaments page

    **Use for**: Work-in-progress tournaments or private collections

    **Default for**: New tournaments/question series
  </Tab>
</Tabs>

### Homepage Display

From `projects/models.py:364-368`:

```python theme={null}
show_on_homepage = models.BooleanField(default=False, db_index=True)
show_on_services_page = models.BooleanField(
    default=False, db_index=True, help_text="Show project on the Services page."
)
```

Featured projects can be promoted to the homepage for maximum visibility.

## Communities

Communities are projects run by external organizations or user groups.

### Community Features

* Independent governance (custom admins)
* Own leaderboards and competitions
* Custom branding (header images, logos)
* Separate permission management
* Can host tournaments within the community

### Creating Communities

Communities are created with `type=COMMUNITY` and automatically get a primary leaderboard (from `projects/models.py:422-439`).

## Indices

Indices aggregate multiple questions into a single metric.

### Index Structure

From `projects/models.py:533-563`:

```python theme={null}
class ProjectIndex(TimeStampedModel):
    class IndexType(models.TextChoices):
        DEFAULT = "default"
        MULTI_YEAR = "multi_year"
    
    type = models.CharField(max_length=32, choices=IndexType.choices)
    min = models.SmallIntegerField(default=-100, help_text="Y-axis min")
    min_label = models.CharField(
        max_length=200,
        help_text='Label at minimum end. Example: "Less democratic"',
    )
    max = models.SmallIntegerField(default=100, help_text="Y-axis max")
    max_label = models.CharField(
        max_length=200,
        help_text='Label at maximum end. Example: "More democratic"',
    )
    increasing_is_good = models.BooleanField(
        default=True,
        help_text="Color polarity: if on, higher values are good (green → right)",
    )
```

### Index Post Weights

Questions contribute to indices with configurable weights (from `projects/models.py:566-614`):

```python theme={null}
class ProjectIndexPost(TimeStampedModel):
    index = models.ForeignKey(ProjectIndex, on_delete=models.CASCADE)
    post = models.ForeignKey("posts.Post", on_delete=models.CASCADE)
    weight = models.FloatField(
        help_text=(
            "Weight of the post within the index. "
            "If the post includes a group of questions, "
            "the same weight will be applied to all subquestions."
        ),
        default=1.0,
    )
    order = models.IntegerField(default=0)
```

**Example**: A democracy index might weight "free elections" at 2.0 and "press freedom" at 1.5.

## Project Metrics

Projects track engagement metrics (from `projects/models.py:375-379`):

```python theme={null}
followers_count = models.PositiveIntegerField(default=0, editable=False)
forecasts_count = models.PositiveIntegerField(default=0, editable=False)
forecasters_count = models.PositiveIntegerField(default=0, editable=False)
```

These are updated via (from `projects/models.py:470-495`):

```python theme={null}
def update_followers_count(self):
    self.followers_count = self.subscriptions.count()

def update_forecasts_count(self):
    result = Post.objects.filter_projects(self).aggregate(
        total_forecasts=Sum("forecasts_count")
    )
    self.forecasts_count = result["total_forecasts"] or 0

def update_forecasters_count(self):
    self.forecasters_count = (
        PostUserSnapshot.objects.filter(
            last_forecast_date__isnull=False,
            user__is_staff=False,
        )
        .filter(Q(post__default_project=self) | Q(post__projects=self))
        .values("user_id")
        .distinct()
        .count()
    )
```

## Subscriptions

Users can follow projects to receive updates (from `projects/models.py:516-530`):

```python theme={null}
class ProjectSubscription(TimeStampedModel):
    user = models.ForeignKey(
        User, on_delete=models.CASCADE, related_name="project_subscriptions"
    )
    project = models.ForeignKey(
        Project, on_delete=models.CASCADE, related_name="subscriptions"
    )
    
    class Meta:
        constraints = [
            models.UniqueConstraint(
                name="projectsubscription_unique_user_project",
                fields=["user_id", "project_id"],
            )
        ]
```

<Tip>
  Subscribe to projects you're interested in to get notifications when new questions are added or important updates occur.
</Tip>

## Querying Projects

Metaculus provides powerful querysets for filtering projects.

### By Type

From `projects/models.py:24-47`:

```python theme={null}
Project.objects.filter_tournament()     # Tournaments and question series
Project.objects.filter_communities()    # Communities
Project.objects.filter_topic()          # Topic tags
Project.objects.filter_category()       # Category tags
Project.objects.filter_leaderboard_tags()  # Leaderboard filtering tags
```

### By Permission

From `projects/models.py:193-209`:

```python theme={null}
# Get all projects user can access
Project.objects.filter_permission(user=user)

# Get projects where user can forecast
Project.objects.filter_permission(user=user, permission=ObjectPermission.FORECASTER)

# Get projects where user is admin
Project.objects.filter_permission(user=user, permission=ObjectPermission.ADMIN)
```

### With Annotations

From `projects/models.py:49-159`:

```python theme={null}
# Annotate with post count
Project.objects.annotate_posts_count()

# Annotate with question count
Project.objects.annotate_questions_count()

# Annotate with user's subscription status
Project.objects.annotate_is_subscribed(user=user)

# Annotate with user's permission level
Project.objects.annotate_user_permission(user=user)
```

## Use Cases

<AccordionGroup>
  <Accordion title="Running a Tournament" icon="medal">
    Create a `TOURNAMENT` project with prize pool, set start/close dates, configure leaderboard, add curated questions.
  </Accordion>

  <Accordion title="Building a Community" icon="users">
    Create a `COMMUNITY` project, set custom permissions, add admins via `ProjectUserPermission`, enable subscriptions.
  </Accordion>

  <Accordion title="Tracking a Topic" icon="tags">
    Create a `TOPIC` or `CATEGORY` project, tag relevant questions via `post.projects`, users can filter by topic.
  </Accordion>

  <Accordion title="Creating an Index" icon="chart-line">
    Create an `INDEX` project, configure scale and labels, add weighted questions via `ProjectIndexPost`.
  </Accordion>

  <Accordion title="Personal Collection" icon="bookmark">
    Create a `PERSONAL_PROJECT`, set `default_permission=null` for privacy, organize questions you're tracking.
  </Accordion>
</AccordionGroup>

## Best Practices

<Steps>
  <Step title="Choose the Right Type">
    Select the project type that matches your use case. Don't use `TOURNAMENT` for simple question collections.
  </Step>

  <Step title="Set Appropriate Permissions">
    Consider who should access your project. Most public projects use `FORECASTER` as default.
  </Step>

  <Step title="Configure Visibility">
    Use `UNLISTED` for work-in-progress, `NOT_IN_MAIN_FEED` for specialized content, `NORMAL` for featured content.
  </Step>

  <Step title="Add Clear Descriptions">
    Write comprehensive descriptions and resolution criteria so participants understand the project goals.
  </Step>

  <Step title="Enable Subscriptions">
    Make your project subscribable so interested users can follow updates and new questions.
  </Step>
</Steps>

## API Reference

<Card title="Projects API" icon="code" href="/api/projects">
  Explore the full Projects API documentation
</Card>

## Related Topics

<CardGroup cols={2}>
  <Card title="Tournaments" icon="medal" href="/features/tournaments">
    Learn about competitive forecasting
  </Card>

  <Card title="Leaderboards" icon="ranking-star" href="/features/leaderboards">
    Understand project-specific rankings
  </Card>

  <Card title="Questions" icon="circle-question" href="/features/questions">
    Learn about organizing questions
  </Card>

  <Card title="Permissions" icon="lock" href="/features/projects#permissions">
    Deep dive into permission management
  </Card>
</CardGroup>
