Skip to content

Commit

Permalink
Start building testing suite for new features
Browse files Browse the repository at this point in the history
  • Loading branch information
leethobbit committed Nov 27, 2024
1 parent 89e5fae commit 354742d
Show file tree
Hide file tree
Showing 27 changed files with 152 additions and 17 deletions.
4 changes: 2 additions & 2 deletions ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ The full span of functionality that Dragonroost will eventually encompass.

This project started out just using normal Django generic Class-based Views (CBVs) - but the constant full page reloads felt archaic, so I decided to pull in htmx (I had been wanting an excuse to learn it, anyways)

- [ ] **Htmx conversion**: Most of the main pages use htmx now, including the dashboard which loads all content in as partials. However, some urls for forms and other bits are not fully converted just yet.
- [X] **Htmx conversion**: Most of the main pages use htmx now, including the dashboard which loads all content in as partials. However, some urls for forms and other bits are not fully converted just yet.
- [ ] **Htmx Reporting Module**: Instead of generating dashboards for each main module, an htmx-driven reporting section will be added instead. The goal is to be able to visualize (and interact with) trends over time for much of the application - animal intakes, adoptions, species trends, volunteer signups, donations over time... basically anything useful for the average small rescue.

## Ongoing work
Expand All @@ -19,7 +19,7 @@ These are the core application modules that will be available on the 1.0.0 relea

- [X] **Animals**: The core module - management of animals and their related traits and information.
- [X] **People**: Management of volunteers, donors, fosters, and adopters.
- [ ] **Business**: Location management, donation tracking, and more.
- [ ] **Business**: Locations (can be buildings or areas of a building), meetings, and more.
- [ ] **Medical**: Medical staff updates, Reporting, vaccine calendar, and more.

Some site-wide features that are planned for 1.0.0
Expand Down
14 changes: 14 additions & 0 deletions dragonroost/animals/managers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
from django.db import models


class AnimalQuerySet(models.QuerySet):
def get_available_animals(self):
return self.filter(status="AVAILABLE")

def get_animals_needing_medical_attention(self):
"""
Return a QuerySet of Animal objects that need medical attention,
i.e. where the status is "MEDICAL_HOLD".
"""

return self.filter(status="MEDICAL_HOLD" / "QUARANTINE")
5 changes: 5 additions & 0 deletions dragonroost/animals/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
from dragonroost.business.models import Location
from dragonroost.people.models import Person

from .managers import AnimalQuerySet

logger = logging.getLogger(__name__)


Expand Down Expand Up @@ -170,8 +172,11 @@ class Animal(models.Model):
default=None,
)

objects = AnimalQuerySet.as_manager()

class Meta:
db_table_comment = "Holds information about animals"
ordering = ["-intake_date"]

def __str__(self):
return self.name
Expand Down
Empty file.
31 changes: 31 additions & 0 deletions dragonroost/animals/tests/factories.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import factory

from dragonroost.animals.models import Species
from dragonroost.business.tests.factories import LocationFactory


class SpeciesFactory(factory.django.DjangoModelFactory):
class Meta:
model = "animals.Species"
django_get_or_create = ("name", "class_name", "diet", "is_ohio_native")

name = factory.Sequence(lambda n: f"Species {n}")
class_name = factory.Faker(
"random_element",
elements=[x[0] for x in Species.CLASS_CHOICES],
)
diet = factory.Faker(
"random_element",
elements=[x[0] for x in Species.DIET_CHOICES],
)
is_ohio_native = factory.Iterator([True, False])


class AnimalFactory(factory.django.DjangoModelFactory):
class Meta:
model = "animals.Animal"
django_get_or_create = ("name", "breed", "species")

name = factory.Faker("first_name")
species = factory.SubFactory(SpeciesFactory)
location = factory.SubFactory(LocationFactory)
Empty file.
Empty file.
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Generated by Django 5.0.9 on 2024-11-27 13:42

import django.db.models.deletion
from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('business', '0001_initial'),
]

operations = [
migrations.CreateModel(
name='TransactionCategory',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=50, unique=True)),
],
options={
'verbose_name_plural': 'Categories',
},
),
migrations.CreateModel(
name='Transaction',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('amount', models.DecimalField(decimal_places=2, default=0.0, max_digits=10)),
('type', models.CharField(choices=[('INCOME', 'Income'), ('EXPENSE', 'Expense')], max_length=10)),
('description', models.TextField(blank=True, default='')),
('date', models.DateTimeField(auto_now_add=True)),
('category', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='business.transactioncategory')),
],
),
migrations.DeleteModel(
name='Donation',
),
]
34 changes: 20 additions & 14 deletions dragonroost/business/models.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
from django.db import models

from dragonroost.people.models import Person


# Create your models here.
class Feedback(models.Model):
Expand Down Expand Up @@ -67,19 +65,27 @@ def get_absolute_url(self):
return f"/business/meetings/{self.id}/"


# class Donation
class Donation(models.Model):
"""
Possible model for donations - not in use currently.
TODO: Change this to a more generic Transaction model.
"""
class TransactionCategory(models.Model):
name = models.CharField(max_length=50, unique=True)

class Meta:
verbose_name_plural = "Categories"

def __str__(self):
return self.name


class Transaction(models.Model):
TRANSACTION_TYPE_CHOICES = [
("INCOME", "Income"),
("EXPENSE", "Expense"),
]

amount = models.DecimalField(max_digits=10, decimal_places=2, default=0.00)
donor = models.ForeignKey(Person, on_delete=models.DO_NOTHING)
is_sponsorship = models.BooleanField(default=False)
donation_date = models.DateTimeField(auto_now_add=True)
category = models.ForeignKey(TransactionCategory, on_delete=models.CASCADE)
type = models.CharField(max_length=10, choices=TRANSACTION_TYPE_CHOICES)
description = models.TextField(blank=True, null=False, default="")
date = models.DateTimeField(auto_now_add=True)

def __str__(self) -> str:
# TODO: Come up with a good string for this return. Maybe donor + amount + date?
return self.pk
def __str__(self):
return f"{self.type} of {self.amount} on {self.date}"
1 change: 0 additions & 1 deletion dragonroost/business/tests.py

This file was deleted.

Empty file.
22 changes: 22 additions & 0 deletions dragonroost/business/tests/factories.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import factory

from dragonroost.business.models import Location
from dragonroost.business.models import Meeting


class LocationFactory(factory.django.DjangoModelFactory):
class Meta:
model = Location
django_get_or_create = ("name",)

name = factory.Sequence(lambda n: f"Location {n}")
description = factory.Sequence(lambda n: f"Description {n}")


class MeetingFactory(factory.django.DjangoModelFactory):
class Meta:
model = Meeting
django_get_or_create = ("title",)

title = factory.Sequence(lambda n: f"Meeting {n}")
description = factory.Sequence(lambda n: f"Description {n}")
Empty file.
Empty file.
Empty file.
20 changes: 20 additions & 0 deletions dragonroost/conftest.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import pytest

from dragonroost.animals.models import Animal
from dragonroost.animals.models import MedicalRecord
from dragonroost.animals.models import Species
from dragonroost.animals.tests.factories import AnimalFactory
from dragonroost.animals.tests.factories import SpeciesFactory
from dragonroost.users.models import User
from dragonroost.users.tests.factories import UserFactory

Expand All @@ -12,3 +17,18 @@ def _media_storage(settings, tmpdir) -> None:
@pytest.fixture
def user(db) -> User:
return UserFactory()


@pytest.fixture
def species(db) -> Species:
return SpeciesFactory.create_batch(5)


@pytest.fixture
def animals(db, species) -> Animal:
return AnimalFactory.create_batch(5)


@pytest.fixture
def medical_records(db, animals) -> MedicalRecord:
return MedicalRecord.objects.filter(animal__in=animals)
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.

0 comments on commit 354742d

Please sign in to comment.