From 8d4dc99a5f761826ba30e255c76f030af77110d0 Mon Sep 17 00:00:00 2001 From: =?utf8?q?J=C3=B6rn=20Menne?= Date: Wed, 5 Feb 2025 11:29:42 +0100 Subject: [PATCH] Model rewrite for category and report finished Currently the loction is not included into the report, since it will become an own model. --- .gitignore | 4 + doc-requirements.txt | 3 + doc/conf.py | 8 +- doc/georeport/models.rst | 28 ++++++- doc/index.rst | 3 + georeport/__init__.py | 1 + georeport/migrations/0001_initial.py | 109 +++++++++++++++++++++++++++ georeport/models.py | 82 ++++++++++++++++++++ georeport/tests.py | 19 ++++- 9 files changed, 254 insertions(+), 3 deletions(-) create mode 100644 doc-requirements.txt create mode 100644 georeport/migrations/0001_initial.py diff --git a/.gitignore b/.gitignore index 1c92262..24ae4f1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,7 @@ __pycache__/ db.sqlite3 + + +doc/_build +doc/build diff --git a/doc-requirements.txt b/doc-requirements.txt new file mode 100644 index 0000000..ca4d1ea --- /dev/null +++ b/doc-requirements.txt @@ -0,0 +1,3 @@ +sphinx +sphinxcontrib-plantuml +-r requirements.txt diff --git a/doc/conf.py b/doc/conf.py index 9056080..743f617 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -16,7 +16,7 @@ django.setup() project = "Pinpoint-Report" copyright = "2025, Jörn Menne" author = "Jörn Menne" -release = "0.1" +release = "0.1.5" # -- General configuration --------------------------------------------------- # https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration @@ -27,6 +27,8 @@ extensions = [ "sphinx.ext.napoleon", "sphinxcontrib.plantuml", "sphinx.ext.mathjax", + "myst_parser", + "sphinx.ext.todo", ] templates_path = ["_templates"] @@ -38,3 +40,7 @@ exclude_patterns = [] html_theme = "alabaster" html_static_path = ["_static"] + + +# Options for todo +todo_include_todos = True diff --git a/doc/georeport/models.rst b/doc/georeport/models.rst index 3d7d447..f233185 100644 --- a/doc/georeport/models.rst +++ b/doc/georeport/models.rst @@ -1,6 +1,32 @@ Models ====== -Here are the models used in Pinpoint-Report +.. todo:: + Split into 2 files. 1 to read and one for the autodoc + +The models represent the structure in the datebase as such, that each model +is a table in the database. +The translation from the *models.py* file to the specifc tables is done by +django with the help of migrations. + +After each alteration of a model-class the following two scripts have to be run. +.. code:: + + python manage migrations + python manage migrate + +The first command creates a migration file, while the second one applies the migration to the database. + +The following models are used: + +* Category +* Report + +Categories are used to group reports and assing the corresponding staff. This is done by preventing access to users, +which have not authority about the category + +.. automodule:: georeport.models + :members: + :undoc-members: diff --git a/doc/index.rst b/doc/index.rst index 92425bb..ed6580e 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -16,3 +16,6 @@ documentation for details. :caption: Contents: georeport/models + + +.. todolist:: diff --git a/georeport/__init__.py b/georeport/__init__.py index e69de29..e6b3e86 100644 --- a/georeport/__init__.py +++ b/georeport/__init__.py @@ -0,0 +1 @@ +version="0.1.5" diff --git a/georeport/migrations/0001_initial.py b/georeport/migrations/0001_initial.py new file mode 100644 index 0000000..4bbdcae --- /dev/null +++ b/georeport/migrations/0001_initial.py @@ -0,0 +1,109 @@ +# Generated by Django 5.1.5 on 2025-02-05 10:09 + +import django.db.models.deletion +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + initial = True + + dependencies = [ + ("auth", "0012_alter_user_first_name_max_length"), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name="Category", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("name", models.CharField(max_length=100)), + ( + "groups", + models.ManyToManyField( + blank=True, related_name="group_owner", to="auth.group" + ), + ), + ( + "parent", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="subcategories", + to="georeport.category", + ), + ), + ( + "user", + models.ManyToManyField( + blank=True, related_name="owner", to=settings.AUTH_USER_MODEL + ), + ), + ], + options={ + "verbose_name_plural": "Categories", + }, + ), + migrations.CreateModel( + name="Report", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("created_at", models.DateTimeField(auto_now=True)), + ("updated_at", models.DateTimeField(auto_now_add=True)), + ("title", models.CharField(max_length=100, unique=True)), + ("descrption", models.TextField(blank=True, null=True)), + ("email", models.EmailField(max_length=254)), + ( + "state", + models.IntegerField( + choices=[ + (0, "New"), + (1, "In Progess"), + (2, "Finished"), + (3, "Archive"), + ], + default=0, + ), + ), + ( + "_oldState", + models.IntegerField( + choices=[ + (0, "New"), + (1, "In Progess"), + (2, "Finished"), + (3, "Archive"), + ], + default=0, + ), + ), + ("published", models.BooleanField(default=False)), + ( + "category", + models.ForeignKey( + on_delete=django.db.models.deletion.RESTRICT, + related_name="reports", + to="georeport.category", + ), + ), + ], + ), + ] diff --git a/georeport/models.py b/georeport/models.py index 71a8362..123f484 100644 --- a/georeport/models.py +++ b/georeport/models.py @@ -1,3 +1,85 @@ +# Copyright: (c) 2025, Jörn Menne +# GNU General Public License v3.0 (see LICSENE or https://www.gnu.org/license/gpl-3.0.md) +""" +Module which contains classes representing the database structure +of the project. +""" + from django.db import models +from django.contrib.auth.models import Group, User +from typing import override # Create your models here. + + +class Category(models.Model): + """ + A class representing a Category in the database. + """ + + # TODO: Prevent circles + name = models.CharField(max_length=100) + parent = models.ForeignKey( + "self", + on_delete=models.CASCADE, + related_name="subcategories", + null=True, + blank=True, + ) + + user = models.ManyToManyField(User, related_name="owner", blank=True) + groups = models.ManyToManyField(Group, related_name="group_owner", blank=True) + + class Meta: + verbose_name_plural = "Categories" + + @override + def __str__(self) -> str: + return str(self.name) + + +class Report(models.Model): + """ + A class representing a Report in the database. + """ + + class State(models.IntegerChoices): + """ + A small class, which provide the possible states of a report + """ + + NEW = 0 + IN_PROGESS = 1 + FINISHED = 2 + ARCHIVE = 3 + + # Timebased autofields + created_at = models.DateTimeField(auto_now=True) + updated_at = models.DateTimeField(auto_now_add=True) + + # Fields to be filled at creation time + category = models.ForeignKey( + Category, on_delete=models.RESTRICT, related_name="reports" + ) + title = models.CharField(max_length=100, unique=True) + descrption = models.TextField(blank=True, null=True) + email = models.EmailField() + # TODO: Images + + # Fields set at creation + state = models.IntegerField(choices=State, default=0) # type: ignore Correct type can not be dtermined + _oldState = models.IntegerField(choices=State, default=0) # type: ignore Correct type can not be dtermined + + """ + The old statevariable is neede to determine, if the state was changed. + """ + published = models.BooleanField(default=False) # type: ignore Correct type can not be dtermined + + @override + def __str__(self) -> str: + return str(self.title) + + +# TODO: Location + +# TODO: Image diff --git a/georeport/tests.py b/georeport/tests.py index 7ce503c..94d3c8f 100644 --- a/georeport/tests.py +++ b/georeport/tests.py @@ -1,3 +1,20 @@ +# Copyright: (c) 2025, Jörn Menne +# GNU General Public License v3.0 (see LICSENE or https://www.gnu.org/license/gpl-3.0.md) + from django.test import TestCase -# Create your tests here. +from .models import Category, Report + + +class ReportTestCase(TestCase): + def setUp(self): + Category.objects.create(name="Cat1") # type:ignore Attribute object is unknown + Report.objects.create( # type:ignore Attribute object is unknown + title="Test", + email="test@test.de", + category=Category.objects.first(), # type:ignore Attribute object is unknown + ) + + def test_unpulished_as_default(self): + report = Report.objects.get(title="Test") # type:ignore Attribute object is unknown + self.assertEqual(report.published, False) -- 2.39.5