-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1 from smdirr/dev
Dev to main
- Loading branch information
Showing
27 changed files
with
724 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
{ | ||
// Use IntelliSense to learn about possible attributes. | ||
// Hover to view descriptions of existing attributes. | ||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 | ||
"version": "0.2.0", | ||
"configurations": [ | ||
{ | ||
"name": "Python Debugger: Django", | ||
"type": "debugpy", | ||
"request": "launch", | ||
"args": [ | ||
"runserver" | ||
], | ||
"django": true, | ||
"autoStartBrowser": false, | ||
"program": "${workspaceFolder}/src/manage.py" | ||
} | ||
] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
[run] | ||
omit = | ||
manage.py |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
[flake8] | ||
max-line-length = 120 | ||
max-complexity = 7 | ||
exclude = | ||
.git, | ||
__pycache__, | ||
old, | ||
.tox, | ||
build, | ||
dist, | ||
venv, | ||
.venv, | ||
env, | ||
.env |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
#!/usr/bin/env python | ||
"""Django's command-line utility for administrative tasks.""" | ||
import os | ||
import sys | ||
|
||
|
||
def main(): | ||
"""Run administrative tasks.""" | ||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'time_tracker.settings') | ||
try: | ||
from django.core.management import execute_from_command_line | ||
except ImportError as exc: | ||
raise ImportError( | ||
"Couldn't import Django. Are you sure it's installed and " | ||
"available on your PYTHONPATH environment variable? Did you " | ||
"forget to activate a virtual environment?" | ||
) from exc | ||
execute_from_command_line(sys.argv) | ||
|
||
|
||
if __name__ == '__main__': | ||
main() |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
from typing import List | ||
from ninja import Router | ||
from django.shortcuts import get_object_or_404 | ||
from person.models import Person | ||
from person.schema import PersonIn, PersonOut | ||
|
||
router = Router() | ||
|
||
|
||
@router.post("/", url_name="person_create") | ||
def create_person(request, payload: PersonIn): | ||
person = Person.objects.create(**payload.dict()) | ||
return {"id": person.id} | ||
|
||
|
||
@router.get("/{person_id}", response=PersonOut, url_name="person_get") | ||
def get_person(request, person_id: int): | ||
person = get_object_or_404(Person, id=person_id) | ||
return person | ||
|
||
|
||
@router.get("/", response=List[PersonOut], url_name="person_list") | ||
def list_persons(request): | ||
qs = Person.objects.all() | ||
return qs | ||
|
||
|
||
@router.put("/{person_id}", url_name="person_edit") | ||
def update_person(request, person_id: int, payload: PersonIn): | ||
person = get_object_or_404(Person, id=person_id) | ||
for attr, value in payload.dict().items(): | ||
setattr(person, attr, value) | ||
person.save() | ||
return {"success": True} | ||
|
||
|
||
@router.delete("/{person_id}", url_name="person_delete") | ||
def delete_person(request, person_id: int): | ||
person = get_object_or_404(Person, id=person_id) | ||
person.delete() | ||
return {"success": True} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
from django.apps import AppConfig | ||
|
||
|
||
class PersonConfig(AppConfig): | ||
default_auto_field = 'django.db.models.BigAutoField' | ||
name = 'person' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
# Generated by Django 5.0.6 on 2024-06-13 11:23 | ||
|
||
import django.db.models.deletion | ||
from django.conf import settings | ||
from django.db import migrations, models | ||
|
||
|
||
class Migration(migrations.Migration): | ||
|
||
initial = True | ||
|
||
dependencies = [ | ||
migrations.swappable_dependency(settings.AUTH_USER_MODEL), | ||
] | ||
|
||
operations = [ | ||
migrations.CreateModel( | ||
name="Person", | ||
fields=[ | ||
( | ||
"id", | ||
models.BigAutoField( | ||
auto_created=True, | ||
primary_key=True, | ||
serialize=False, | ||
verbose_name="ID", | ||
), | ||
), | ||
("first_name", models.CharField(max_length=100)), | ||
("last_name", models.CharField(max_length=100)), | ||
("birth_date", models.DateField(null=True)), | ||
("email", models.EmailField(max_length=254, unique=True)), | ||
( | ||
"user", | ||
models.ForeignKey( | ||
null=True, | ||
on_delete=django.db.models.deletion.CASCADE, | ||
to=settings.AUTH_USER_MODEL, | ||
), | ||
), | ||
], | ||
), | ||
] |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
from django.db import models | ||
from django.contrib.auth import models as models_auth | ||
|
||
|
||
class Person(models.Model): | ||
first_name = models.CharField(max_length=100, null=False) | ||
last_name = models.CharField(max_length=100, null=False) | ||
birth_date = models.DateField(null=True) | ||
email = models.EmailField(max_length=254, null=False, unique=True) | ||
|
||
user = models.ForeignKey(to=models_auth.User, on_delete=models.CASCADE, null=True) | ||
|
||
def __str__(self): | ||
return f"{self.last_name}, {self.first_name}" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
from datetime import date | ||
from ninja import Schema | ||
|
||
|
||
class PersonIn(Schema): | ||
first_name: str | ||
last_name: str | ||
email: str | ||
birth_date: date = None | ||
|
||
|
||
class PersonOut(Schema): | ||
id: int | ||
first_name: str | ||
last_name: str | ||
email: str | ||
# birth_date: date = None |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,200 @@ | ||
from django.test import TestCase, Client | ||
from django.urls import reverse | ||
from person.models import Person | ||
|
||
|
||
class TestPerson(TestCase): | ||
|
||
def setUp(self) -> None: | ||
super().setUp() | ||
self.client = Client() | ||
my_pass = "devtestlocal" | ||
data = { | ||
"first_name": "Thomas", | ||
"last_name": "Anderson", | ||
"email": "tand@mail.me", | ||
"password": f"{my_pass}", | ||
"confirm_password": f"{my_pass}", | ||
} | ||
self.client.post( | ||
reverse("register"), data=data, content_type="application/json" | ||
) | ||
token = self.client.post( | ||
reverse("token_obtain_pair"), | ||
{"username": data.get("email"), "password": f"{my_pass}"}, | ||
content_type="application/json", | ||
) | ||
self.access_token = token.json().get("access") | ||
|
||
def test_get_person(self): | ||
|
||
Person.objects.create(first_name="John", last_name="Doe", email="john@test.com") | ||
Person.objects.create( | ||
first_name="Alice", last_name="Silver", email="asilver@test.com" | ||
) | ||
|
||
response = self.client.get( | ||
reverse("api:person_list"), | ||
headers={"Authorization": f"Bearer {self.access_token}"}, | ||
) | ||
|
||
self.assertEqual(response.status_code, 200) | ||
self.assertEqual(len(response.json()), 2) | ||
self.assertEqual(response.json()[0]["first_name"], "John") | ||
self.assertEqual(response.json()[0]["email"], "john@test.com") | ||
|
||
def test_create_person(self): | ||
|
||
data = { | ||
"first_name": "Jenifer", | ||
"last_name": "Lopez", | ||
"email": "jlo@mail.com", | ||
"birth_date": "1970-03-21", | ||
} | ||
response = self.client.post( | ||
reverse("api:person_create"), | ||
data=data, | ||
content_type="application/json", | ||
headers={"Authorization": f"Bearer {self.access_token}"}, | ||
) | ||
|
||
self.assertEqual(response.status_code, 200) | ||
self.assertGreater(response.json().get("id"), 0) | ||
|
||
def test_update_person(self): | ||
data = { | ||
"first_name": "Jenifer", | ||
"last_name": "Lopez", | ||
"email": "jlo@mail.com", | ||
"birth_date": "1970-03-21", | ||
} | ||
response = self.client.post( | ||
reverse("api:person_create"), | ||
data=data, | ||
content_type="application/json", | ||
headers={"Authorization": f"Bearer {self.access_token}"}, | ||
) | ||
|
||
self.assertEqual(response.status_code, 200) | ||
id = response.json().get("id") | ||
url = reverse("api:person_edit", kwargs={"person_id": id}) | ||
data["last_name"] = "López" | ||
response = self.client.put( | ||
url, | ||
data=data, | ||
content_type="application/json", | ||
headers={"Authorization": f"Bearer {self.access_token}"}, | ||
) | ||
|
||
self.assertEqual(response.status_code, 200) | ||
self.assertEqual(response.json().get("success"), True) | ||
person = self.client.get( | ||
reverse("api:person_get", args=[id]), | ||
headers={"Authorization": f"Bearer {self.access_token}"}, | ||
) | ||
|
||
self.assertEqual(person.status_code, 200) | ||
self.assertEqual(person.json()["last_name"], "López") | ||
|
||
def test_person_delete(self): | ||
data = { | ||
"first_name": "Jenifer", | ||
"last_name": "Lopez", | ||
"email": "jlo@mail.com", | ||
"birth_date": "1970-03-21", | ||
} | ||
response = self.client.post( | ||
reverse("api:person_create"), | ||
data=data, | ||
content_type="application/json", | ||
headers={"Authorization": f"Bearer {self.access_token}"}, | ||
) | ||
|
||
self.assertEqual(response.status_code, 200) | ||
id = response.json().get("id") | ||
url = reverse("api:person_delete", kwargs={"person_id": id}) | ||
response = self.client.delete( | ||
url, | ||
headers={"Authorization": f"Bearer {self.access_token}"}, | ||
) | ||
self.assertEqual(response.status_code, 200) | ||
self.assertEqual(response.json().get("success"), True) | ||
|
||
def test_get_person_401(self): | ||
response = self.client.get(reverse("api:person_list")) | ||
self.assertEqual(response.status_code, 401) | ||
|
||
def test_get_persons(self): | ||
|
||
Person.objects.create(first_name="John", last_name="Doe", email="john@test.com") | ||
|
||
response = self.client.get( | ||
reverse("api:person_list"), | ||
headers={"Authorization": f"Bearer {self.access_token}"}, | ||
) | ||
|
||
self.assertEqual(response.status_code, 200) | ||
self.assertEqual(len(response.json()), 1) | ||
self.assertEqual(response.json()[0]["email"], "john@test.com") | ||
|
||
def test_retrieve_person(self): | ||
|
||
my_person = Person.objects.create( | ||
first_name="Juan", last_name="Fangio", email="jf@test.com" | ||
) | ||
response = self.client.get( | ||
reverse("api:person_get", args=[my_person.id]), | ||
headers={"Authorization": f"Bearer {self.access_token}"}, | ||
) | ||
|
||
self.assertEqual(response.status_code, 200) | ||
self.assertEqual(response.json()["email"], "jf@test.com") | ||
|
||
def test_str_person(self): | ||
|
||
person = Person.objects.create( | ||
first_name="John", last_name="Doe", email="john@test.com" | ||
) | ||
self.assertEqual(str(person), "Doe, John") | ||
|
||
def test_register_pass_no_match(self): | ||
data = { | ||
"first_name": "Mick", | ||
"last_name": "Jaegger", | ||
"email": "mj@mail.me", | ||
"password": "match", | ||
"confirm_password": "no match", | ||
} | ||
response = self.client.post( | ||
reverse("register"), data=data, content_type="application/json" | ||
) | ||
self.assertEqual(response.status_code, 400) | ||
self.assertEqual(response.json().get("error"), "Passwords do not match") | ||
|
||
def test_register_username_already_exist(self): | ||
data = { | ||
"first_name": "Mick", | ||
"last_name": "Jaegger", | ||
"email": "tand@mail.me", | ||
"password": "test@1234", | ||
"confirm_password": "test@1234", | ||
} | ||
response = self.client.post( | ||
reverse("register"), data=data, content_type="application/json" | ||
) | ||
self.assertEqual(response.status_code, 400) | ||
self.assertEqual(response.json().get("error"), "Username already taken") | ||
|
||
def test_register_invalid_body(self): | ||
invalid_json = "{email: 'test@example.com', password: 'pass123', confirm_password: 'pass123'" | ||
response = self.client.post( | ||
reverse("register"), data=invalid_json, content_type="application/json" | ||
) | ||
self.assertEqual(response.status_code, 400) | ||
self.assertEqual(response.json().get("error"), "Invalid JSON") | ||
|
||
def test_register_invalid_request_method(self): | ||
|
||
response = self.client.get(reverse("register"), content_type="application/json") | ||
self.assertEqual(response.status_code, 405) | ||
self.assertEqual(response.json().get("error"), "Invalid request method") |
Oops, something went wrong.