diff --git a/.flake8 b/.flake8 index add395f6..f76d6a60 100644 --- a/.flake8 +++ b/.flake8 @@ -12,3 +12,4 @@ ignore = exclude = __pycache__ testing.py + migrations diff --git a/poetry.lock b/poetry.lock index cce542d6..ba2e84a9 100644 --- a/poetry.lock +++ b/poetry.lock @@ -7,7 +7,7 @@ python-versions = "*" version = "1.4.3" [[package]] -category = "dev" +category = "main" description = "Atomic file writes." marker = "sys_platform == \"win32\"" name = "atomicwrites" @@ -16,7 +16,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" version = "1.4.0" [[package]] -category = "dev" +category = "main" description = "Classes Without Boilerplate" name = "attrs" optional = false @@ -145,9 +145,9 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" version = "7.1.2" [[package]] -category = "dev" +category = "main" description = "Cross-platform colored terminal text." -marker = "platform_system == \"Windows\" or sys_platform == \"win32\"" +marker = "sys_platform == \"win32\" or platform_system == \"Windows\"" name = "colorama" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" @@ -575,7 +575,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" version = "2.9" [[package]] -category = "dev" +category = "main" description = "Read metadata from Python packages" marker = "python_version < \"3.8\"" name = "importlib-metadata" @@ -695,7 +695,7 @@ python-versions = "*" version = "0.6.1" [[package]] -category = "dev" +category = "main" description = "More routines for operating on iterables, beyond itertools" name = "more-itertools" optional = false @@ -760,7 +760,7 @@ python-versions = ">=3.5" version = "7.1.2" [[package]] -category = "dev" +category = "main" description = "plugin and hook calling mechanisms for python" name = "pluggy" optional = false @@ -784,7 +784,7 @@ python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*" version = "2.8.5" [[package]] -category = "dev" +category = "main" description = "library with cross-python path, ini-parsing, io, code, log facilities" name = "py" optional = false @@ -845,7 +845,7 @@ python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" version = "2.4.7" [[package]] -category = "dev" +category = "main" description = "pytest: simple powerful testing with Python" name = "pytest" optional = false @@ -1187,7 +1187,7 @@ secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "pyOpenSSL (>=0 socks = ["PySocks (>=1.5.6,<1.5.7 || >1.5.7,<2.0)"] [[package]] -category = "dev" +category = "main" description = "Measures number of Terminal column cells of wide-character codes" name = "wcwidth" optional = false @@ -1203,7 +1203,7 @@ python-versions = "*" version = "1.12.1" [[package]] -category = "dev" +category = "main" description = "Backport of pathlib-compatible object wrapper for zip files" marker = "python_version < \"3.8\"" name = "zipp" @@ -1216,7 +1216,7 @@ docs = ["sphinx", "jaraco.packaging (>=3.2)", "rst.linker (>=1.9)"] testing = ["jaraco.itertools", "func-timeout"] [metadata] -content-hash = "8a170a2eb16d83f52711ab8c6d74b3ac9ce55a877ffc8f6d10b3da05b66e60fe" + python-versions = "^3.7" [metadata.files] diff --git a/src/api/admin.py b/src/api/admin.py index 413e7154..f7cbcbe7 100644 --- a/src/api/admin.py +++ b/src/api/admin.py @@ -5,6 +5,7 @@ Location, Scholarship, ScholarshipApplication, + SuccessStory, TeamMember, ) @@ -73,3 +74,13 @@ class CodeSchoolAdmin(admin.ModelAdmin): ) search_fields = ("name", "rep_name", "rep_email", "url") + + +@admin.register(SuccessStory) +class SuccessStoryAdmin(admin.ModelAdmin): + list_display = ( + "created_by", + "created_at", + "text", + "is_approved", + ) diff --git a/src/api/migrations/0009_successstory.py b/src/api/migrations/0009_successstory.py new file mode 100644 index 00000000..adea82aa --- /dev/null +++ b/src/api/migrations/0009_successstory.py @@ -0,0 +1,31 @@ +# Generated by Django 2.2.10 on 2020-05-01 14:35 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("api", "0008_codeschool_is_vet_tec_approved"), + ] + + operations = [ + migrations.CreateModel( + name="SuccessStory", + fields=[ + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("created_by", models.CharField(blank=True, max_length=256, null=True)), + ("created_at", models.DateTimeField(auto_now_add=True)), + ("text", models.TextField(blank=True, null=True)), + ("is_approved", models.BooleanField(blank=True, null=True)), + ], + ), + ] diff --git a/src/api/migrations/0010_auto_20200505_1711.py b/src/api/migrations/0010_auto_20200505_1711.py new file mode 100644 index 00000000..a0ca4a3d --- /dev/null +++ b/src/api/migrations/0010_auto_20200505_1711.py @@ -0,0 +1,33 @@ +# Generated by Django 2.2.10 on 2020-05-05 17:11 + +import django.db.models.deletion +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("api", "0009_successstory"), + ] + + operations = [ + migrations.AlterModelOptions( + name="successstory", options={"verbose_name_plural": "Success Stories"}, + ), + migrations.AlterField( + model_name="successstory", + name="created_by", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.DO_NOTHING, + to=settings.AUTH_USER_MODEL, + ), + ), + migrations.AlterField( + model_name="successstory", + name="is_approved", + field=models.BooleanField(default=False), + ), + ] diff --git a/src/api/models.py b/src/api/models.py index f80adb05..52b43ca2 100644 --- a/src/api/models.py +++ b/src/api/models.py @@ -92,3 +92,22 @@ def __str__(self): class Meta: db_table = "api_team_members" + + +class SuccessStory(models.Model): + """ + Model that holds user success stories. + """ + + created_by = models.ForeignKey( + User, models.DO_NOTHING, blank=True, null=True, unique=False + ) + created_at = models.DateTimeField(auto_now_add=True, unique=False) + text = models.TextField(blank=True, null=True, unique=False) + is_approved = models.BooleanField(default=False, unique=False) + + def __str__(self): + return f"{self.created_by} - {self.is_approved}" + + class Meta: + verbose_name_plural = "Success Stories" diff --git a/src/api/serializers.py b/src/api/serializers.py index 457789c0..66320f88 100644 --- a/src/api/serializers.py +++ b/src/api/serializers.py @@ -5,6 +5,7 @@ Location, Scholarship, ScholarshipApplication, + SuccessStory, TeamMember, ) @@ -39,3 +40,9 @@ class TeamMemberSerializer(serializers.HyperlinkedModelSerializer): class Meta: model = TeamMember fields = "__all__" + + +class SuccessStorySerializer(serializers.HyperlinkedModelSerializer): + class Meta: + model = SuccessStory + fields = "__all__" diff --git a/src/api/urls.py b/src/api/urls.py index 0f3c2eb4..00f31ec7 100644 --- a/src/api/urls.py +++ b/src/api/urls.py @@ -9,5 +9,6 @@ router.register("scholarships", views.ScholarshipViewSet) router.register("scholarshipApplications", views.ScholarshipApplicationViewSet) router.register("teamMembers", views.TeamMemberViewSet) +router.register("sucessStory", views.SuccessStoryViewSet) urlpatterns = [path("", include(router.urls))] diff --git a/src/api/views.py b/src/api/views.py index 22c260b3..faace9be 100644 --- a/src/api/views.py +++ b/src/api/views.py @@ -1,11 +1,12 @@ -from rest_framework.permissions import IsAuthenticated -from rest_framework.viewsets import ReadOnlyModelViewSet +from rest_framework.permissions import IsAuthenticated, IsAuthenticatedOrReadOnly +from rest_framework.viewsets import ModelViewSet, ReadOnlyModelViewSet from api.models import ( CodeSchool, Location, Scholarship, ScholarshipApplication, + SuccessStory, TeamMember, ) from api.serializers import ( @@ -13,6 +14,7 @@ LocationSerializer, ScholarshipApplicationSerializer, ScholarshipSerializer, + SuccessStorySerializer, TeamMemberSerializer, ) @@ -41,3 +43,9 @@ class ScholarshipApplicationViewSet(ReadOnlyModelViewSet): class TeamMemberViewSet(ReadOnlyModelViewSet): serializer_class = TeamMemberSerializer queryset = TeamMember.objects.all() + + +class SuccessStoryViewSet(ModelViewSet): + serializer_class = SuccessStorySerializer + queryset = SuccessStory.objects.all() + permission_classes = (IsAuthenticatedOrReadOnly,) diff --git a/src/tests/factories.py b/src/tests/factories.py index 6cf9a374..fac9e276 100644 --- a/src/tests/factories.py +++ b/src/tests/factories.py @@ -1,3 +1,4 @@ +import datetime import threading from allauth.account.models import EmailAddress @@ -13,7 +14,9 @@ SubFactory, django, ) +from faker import Faker +from api.models import SuccessStory from core.models import Profile from tests.test_data import ( DEFAULT_PASSWORD, @@ -21,6 +24,7 @@ random_branch, random_mos, random_pay_grade, + random_text, ) @@ -86,3 +90,12 @@ class Meta: ) profile = RelatedFactory(ProfileFactory, "user") active_email = RelatedFactory(EmailAddressFactory, "user", email=email) + + +class SuccessFactory(DjangoModelFactory): + class Meta: + model = SuccessStory + + created_at = datetime.datetime.now() + text = LazyFunction(fake.paragraph) + is_approved = LazyFunction(fake.pybool) diff --git a/src/tests/fixtures.py b/src/tests/fixtures.py index 8f0f2c4d..72e91ae1 100644 --- a/src/tests/fixtures.py +++ b/src/tests/fixtures.py @@ -85,3 +85,11 @@ def random_profile_dict(request): profile = request.param profile.pop("user") return request.param + + +@pytest.fixture +def success_story(db, user): + story = f.SuccessFactory() + story.created_by = user + story.save() + return story diff --git a/src/tests/integration/test_success_story.py b/src/tests/integration/test_success_story.py new file mode 100644 index 00000000..b61dba23 --- /dev/null +++ b/src/tests/integration/test_success_story.py @@ -0,0 +1,14 @@ +import pytest +from django.db import models + +from api.models import SuccessStory +from tests.factories import SuccessFactory + + +@pytest.mark.django_db +def test_stories(success_story): + db = SuccessStory.objects.all() + assert db.get(created_by=success_story.created_by) + assert db.get(created_at=success_story.created_at) + assert db.get(text=success_story.text) + assert db.get(is_approved=success_story.is_approved) diff --git a/src/tests/test_data.py b/src/tests/test_data.py index 2b3cabbf..43abb50d 100644 --- a/src/tests/test_data.py +++ b/src/tests/test_data.py @@ -13,6 +13,10 @@ def random_branch(): ) +def random_text(): + return fake.random_text(max_nb_chars=200) + + def random_pay_grade(): return f"{fake.random_element(('E', 'O'))}{fake.random_digit()}"