Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
# =========================
# Sphinx documentation
# =========================
docs/_build/

# =========================
# Python / general
# =========================
Expand Down
44 changes: 44 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,50 @@ docker compose down
```
---

## Documentation

Developer documentation is built with [Sphinx](https://www.sphinx-doc.org/) from the [`docs/`](docs/) directory (autodoc, Google-style docstrings via Napoleon, viewcode).

**Install dependencies** (includes Sphinx):

```bash
pip install -r requirements.txt
```

**Generate HTML** (either command from the repository root):

```bash
cd docs && make html
```

```bash
sphinx-build -b html docs docs/_build
```

Open `docs/_build/html/index.html` in a browser.

**Rebuild after code or docstring changes** (clean output, then build again):

```bash
cd docs && make clean html
```

Or remove the build directory and run `sphinx-build` again:

```bash
rm -rf docs/_build && sphinx-build -b html docs docs/_build
```

**Using Docker** (from the project root, with the stack running and the app image built):

```bash
docker compose exec web sphinx-build -b html docs docs/_build
```

The `docs/_build/` directory is gitignored.

---

## 🚀 Stack
- Django, REST, Channels
- Redis
Expand Down
50 changes: 6 additions & 44 deletions accounts/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,38 +13,11 @@
class User(AbstractUser):
"""Extended user model for the Durak card game application.

This model extends Django's ``AbstractUser`` to support additional
game-specific fields and convenience methods. A UUID is used as a
primary key to improve security, avoid predictable identifiers, and
support distributed systems.

Attributes:
id (UUIDField): Primary key using UUID4.
avatar_url (URLField): Optional URL to the user's avatar image.
created_at (DateTimeField): Timestamp of when the user account was created.

Inherited Attributes from ``AbstractUser``:
username, email, password, first_name, last_name,
is_active, is_staff, is_superuser,
date_joined, last_login

Reverse Relations:
sent_messages (QuerySet[Message]): Messages sent by the user.
received_messages (QuerySet[Message]): Private messages received by the user.
lobby_set (QuerySet[Lobby]): Lobbies created by the user.
lobbyplayer_set (QuerySet[LobbyPlayer]): Lobby participation records.
gameplayer_set (QuerySet[GamePlayer]): Game participation records.
playerhand_set (QuerySet[PlayerHand]): Cards owned by the user in a match.
turn_set (QuerySet[Turn]): Turns made by the user.

Example:
user = User.objects.create_user(
username="player1",
email="player1@example.com",
password="secure_password"
)
user.avatar_url = "https://example.com/avatar.jpg"
user.save()
Extends Django ``AbstractUser`` with game-specific fields and helpers.
Uses a UUID primary key. Inherits standard auth fields from ``AbstractUser``
(username, email, password, ``is_active``, ``is_staff``, etc.).
Reverse relations include lobby membership, game participation, messages,
hands, and turns.
"""

id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
Expand Down Expand Up @@ -204,18 +177,7 @@ class Block(models.Model):

A block prevents the ``blocked`` user from interacting with the
``blocker`` (e.g., sending messages, joining their lobby, sending invites).

Attributes:
blocker (ForeignKey[User]): The user who initiated the block.
blocked (ForeignKey[User]): The user who is being blocked.
created_at (DateTimeField): Timestamp of when the block was created.

Constraints:
- A user cannot block the same user more than once (unique_together).
- Indexed lookups for efficient permission checks.

Example:
Block.objects.create(blocker=user1, blocked=user2)
The pair ``(blocker, blocked)`` is unique and indexed for lookups.
"""

blocker = models.ForeignKey(
Expand Down
31 changes: 3 additions & 28 deletions chat/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,17 +87,6 @@ class Chat(models.Model):
- automatically created lobby chats (is_lobby=True)

Messages are always attached to a Chat, not directly to a Lobby or users.

Attributes:
id (UUID): Unique identifier for the chat.
name (str): Optional name (e.g. "Lobby #1").
description (str): Optional description.
is_group (bool): Whether the chat supports multiple participants.
is_lobby (bool): Whether the chat belongs to a game lobby.
is_global (bool): Whether this is a global/world channel (not lobby-bound).
lobby (ForeignKey): Optional reference to a Lobby object.
dm_pair_key (str): For 1-on-1 chats only; stable key for the user pair.
created_at (datetime): Timestamp of creation.
"""

objects = ChatManager()
Expand Down Expand Up @@ -229,12 +218,6 @@ class ChatParticipant(models.Model):
"""Represents a user's membership in a chat with assigned permissions.

Each user can belong to multiple chats and have different roles in each.

Attributes:
chat (Chat): The chat the user participates in.
user (User): The participating user.
role (str): Permission level ("owner", "admin", "member").
joined_at (datetime): When the user joined the chat.
"""

ROLE_CHOICES = [
Expand Down Expand Up @@ -282,20 +265,12 @@ def demote(self):
self.role = "member"
self.save(update_fields=["role"])


class Message(models.Model):
"""Represents a text message inside a chat.

Messages belong strictly to a Chat instance. Lobby messages and private
messages are simply different chat types — there are no separate fields
for lobby/receiver.

Attributes:
id (UUID): Unique message identifier.
sender (User): The user who sent the message.
chat (Chat): Chat to which the message belongs.
content (str): Text content.
sent_at (datetime): Timestamp of message creation.
Messages belong strictly to a ``Chat`` instance. Lobby and direct messages
differ by chat type only; there are no separate lobby or receiver fields
on this model.
"""

id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
Expand Down
15 changes: 15 additions & 0 deletions docs/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Minimal makefile for Sphinx documentation
#

SPHINXOPTS ?=
SPHINXBUILD ?= sphinx-build
SOURCEDIR = .
BUILDDIR = _build

.PHONY: help Makefile

help:
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)

%: Makefile
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
Empty file added docs/_static/.gitkeep
Empty file.
38 changes: 38 additions & 0 deletions docs/conf.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# pylint: skip-file
"""Sphinx configuration for Fools_Arena."""

import os
import sys

sys.path.insert(0, os.path.abspath(".."))
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "Fools_Arena.settings")

import django

django.setup()

project = "Fools_Arena"
copyright = "2026, Maksim Bayarchuk, Kiryl Alishkevich, Aliaksandr Saroka"
author = "Maksim Bayarchuk, Kiryl Alishkevich, Aliaksandr Saroka"
release = "0.1"

extensions = [
"sphinx.ext.autodoc",
"sphinx.ext.napoleon",
"sphinx.ext.viewcode",
]

templates_path = ["_templates"]
exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"]

napoleon_google_docstring = True
napoleon_numpy_docstring = False

html_theme = "alabaster"
html_static_path = ["_static"]

autodoc_default_options = {
"members": True,
"undoc-members": True,
"show-inheritance": True,
}
18 changes: 18 additions & 0 deletions docs/index.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
Fools_Arena developer documentation
===================================

Online multiplayer **Durak** (Fool) card game — Django and Django Channels.

.. toctree::
:maxdepth: 2
:caption: Reference

models
views

Indices and tables
==================

* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`
8 changes: 8 additions & 0 deletions docs/models.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
Models
======

.. automodule:: accounts.models

.. automodule:: chat.models

.. automodule:: game.models
12 changes: 12 additions & 0 deletions docs/views.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
Views
=====

.. automodule:: accounts.views

.. automodule:: accounts.api_views

.. automodule:: chat.views

.. automodule:: chat.api_views

.. automodule:: game.views
Loading
Loading