Skip to content
Closed
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
49 changes: 49 additions & 0 deletions .github/workflows/python-app.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# This workflow will install Python dependencies, run tests and lint with a single version of Python
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python

name: Python application

on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]

permissions:
contents: read

jobs:
build:

runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4
- name: Set up Python 3.10
uses: actions/setup-python@v3
with:
python-version: "3.12.3"
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install flake8 djangorestframework psycopg2-binary pytest-django
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
- name: Lint with flake8
run: |
# stop the build if there are Python syntax errors or undefined names
flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
# exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
- name: Create test settings.py
run: |
cp backend/settings_template.txt backend/settings.py
# use sed to use SQLite for testing
sed -i "s/'ENGINE': 'django.db.backends.postgresql'/'ENGINE': 'django.db.backends.sqlite3'/g" backend/settings.py
sed -i "s/'NAME': 'your_database_name'/'NAME': ':memory:'/g" backend/settings.py
- name: Make and run migrations
run: |
python manage.py makemigrations
python manage.py migrate
- name: Test with pytest
run: |
pytest
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ backend/__pycache/*
# personal settings
backend/settings.py

# pytest
.pytest_cache/
*.py[cod]

# Personal Migrations
backend/quickstart/migrations/*

Expand Down
103 changes: 102 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,13 @@ This will generate the build files in the `build` directory.
- `tsconfig.json`: TypeScript configuration file.
- `package.json`: Contains the project dependencies and scripts.

## Backend (Windows/Linux)
### Contributing

When making a pull request to the frontend, you *must* follow these rules to ensure your PR is not automatically rejected:
- Pull requests must be made to the `dev` branch, *NOT* `main`.
- Pull requests must include an image of the changes made to the frontend.

## Backend (Windows/ Generic Linux)

### Prerequisites
This guide expects that you have [Python](https://www.python.org/downloads/) (At least 3.12.0) installed.
Expand Down Expand Up @@ -233,3 +239,98 @@ If Python was installed via either Homebrew or the official Python installer, yo
```

If it all goes well, cool stuff, it's working!

### Setup (Arch Linux)

- This guide assumes an installation using `systemd`. If you're using something else like OpenRC or runit, tweak the `systemctl` commands accordingly.

1. **Create the venv**

```sh
cd Studygatchi
python3 -m venv env
```

```sh
source ./env/bin/activate
```


2. **Run the following command to install Django and the Django Rest Framework:**

```sh
pip install django djangorestframework
```

3. **Install PostgreSQL (version 17)**

```sh
pacman -Syu postgresql
```


4. **Activate the venv (if it's not already active) using the corresponding command and install psycopg2**

```sh
pip install psycopg2-binary
```

We will use this to be able to connect Django with Postgres!

5. **Initialize the database cluster**

```sh
sudo -u postgres initdb -D /var/lib/postgres/data
```

6. **Start the PostgreSQL service**

```sh
sudo systemctl start postgresql
```

- It's optional, but recommended that you have PostgreSQL run at startup:

```sh
sudo systemctl enable postgresql
```

7. **Access the PostgreSQL shell, logged in as the superuser:**

```sh
psql -U postgres
```

8. **Run the following SQL commands:**

```sql
CREATE USER <myprojectuser> WITH PASSWORD '<your_secure_password>';
CREATE DATABASE studygatchi_db OWNER <myprojectuser>;
GRANT ALL PRIVILEGES ON DATABASE studygatchi_db TO <myprojectuser>;
\q
```
Replace `<myprojectuser>` with whatever username you want, and likewise for the password.

9. **In the backend directory, create a file called `settings.py` with the contents of `settings_template.txt`.**

```sh
cd backend/
cp settings_template.txt settings.py
```

10. **In `settings.py`, go to where it says `DATABASES`, and insert your info from step 6 into the corresponding places.**

11. **At the root of the project, run the following commands** ***with the venv active*** **to apply migrations:**

```sh
python3 manage.py makemigrations
python3 manage.py migrate
```

12. **Test the connection by running this command:**

```sh
python3 manage.py runserver
```

If it all goes well, cool stuff; it's working!
1 change: 1 addition & 0 deletions backend/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@

urlpatterns = [
path("ping/", ping),
path("create_task/", create_task),
]
4 changes: 4 additions & 0 deletions backend/quickstart/admin.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from .models import StudyUser

admin.site.register(StudyUser, UserAdmin)

# Register your models here.
6 changes: 3 additions & 3 deletions backend/quickstart/models.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
from django.db import models
from django.contrib.auth.models import User
from django.contrib.auth.models import AbstractUser

# Create your models here.

class StudyUser(User):
class StudyUser(AbstractUser):
# inherits: username, password, email, ...
money = models.IntegerField(default=100)

Expand All @@ -14,4 +14,4 @@ class Task(models.Model):
category = models.TextField(null=True)
due_date = models.DateTimeField()
description = models.TextField()
username = models.ForeignKey(StudyUser, on_delete=models.CASCADE)
user = models.ForeignKey(StudyUser, on_delete=models.CASCADE)
11 changes: 11 additions & 0 deletions backend/quickstart/serializers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
from .models import Task
from rest_framework import serializers

# serializers are cool because they allow you to skip having to deal with the model constructors

class TaskSerializer(serializers.HyperlinkedModelSerializer):
user = serializers.HiddenField(default=serializers.CurrentUserDefault())

class Meta:
model = Task
fields = ["reward", "name", "category", "due_date", "description", "user"]
54 changes: 53 additions & 1 deletion backend/quickstart/tests.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,55 @@
from django.test import TestCase
# Some tests created with aid from Gemini
import pytest
from rest_framework import status
from .models import StudyUser, Task

@pytest.fixture
def api_client():
from rest_framework.test import APIClient
return APIClient()

@pytest.fixture
def test_user(db):
"""Creates a StudyUser for testing."""
return StudyUser.objects.create_user(
username="andres",
password="password123",
money=500
)

@pytest.mark.django_db
class TestTaskCreation:
def test_create_task_authenticated(self, api_client, test_user):
# 1. Authenticate
api_client.force_authenticate(user=test_user)

# 2. Prepare Data (No user info in JSON, handled by CurrentUserDefault)
url = "/api/create_task/" # Ensure this matches your urls.py
data = {
"name": "Math Homework",
"reward": 50,
"description": "Finish algebra 1",
"due_date": "2026-12-31"
}

# 3. Request
response = api_client.post(url, data, format='json')

# 4. Assertions
assert response.status_code == status.HTTP_201_CREATED

# Verify DB entry
task = Task.objects.get(name="Math Homework")
assert task.user == test_user
assert task.user.money == 500

def test_create_task_unauthenticated(self, api_client):
"""Ensure logged-out users can't create tasks."""
url = "/api/create_task/"
data = {"name": "Ghost Task"}

response = api_client.post(url, data, format='json')

assert response.status_code == status.HTTP_403_FORBIDDEN

# Create your tests here.
26 changes: 20 additions & 6 deletions backend/quickstart/views.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,27 @@
from django.shortcuts import render
from rest_framework.response import Response
from rest_framework.decorators import api_view
from rest_framework.decorators import api_view, permission_classes
from rest_framework.permissions import IsAuthenticated
from rest_framework import status
from .models import StudyUser

from .models import StudyUser # remove, should be serializer
from .serializers import TaskSerializer

# Create your views here.

@api_view(['GET',])
@api_view(['GET'])
def ping(request):
if request.method == 'GET':
print(type(StudyUser))
return Response(b"pong")
print(type(StudyUser))
return Response("pong")

@api_view(['POST']) # Some of this function was made with the help of Gemini
@permission_classes([IsAuthenticated])
def create_task(request):
serializer = TaskSerializer(data=request.data, context={"request": request})

if serializer.is_valid():
# save and return
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)

return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
2 changes: 1 addition & 1 deletion backend/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
django
djangorestframework
psycopg2-binary

pytest-django
2 changes: 2 additions & 0 deletions backend/settings_template.txt
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,8 @@ DATABASES = {
}
}

AUTH_USER_MODEL = "quickstart.StudyUser"


# Password validation
# https://docs.djangoproject.com/en/5.2/ref/settings/#auth-password-validators
Expand Down
Binary file removed db.sqlite3
Binary file not shown.
Loading
Loading