simplified project request model

This commit is contained in:
Oliver Zander 2025-10-15 12:19:25 +02:00
parent dcd9a3d213
commit d1cda4c1a9
3 changed files with 42 additions and 104 deletions

View File

@ -1,8 +1,9 @@
# Generated by Django 5.2.5 on 2025-09-24 16:58 # Generated by Django 5.2.5 on 2025-10-15 10:14
import django.core.validators
from django.db import migrations, models from django.db import migrations, models
from input.models import validate_cost
class Migration(migrations.Migration): class Migration(migrations.Migration):
@ -25,32 +26,33 @@ class Migration(migrations.Migration):
('name', models.CharField(max_length=200, verbose_name='Name des Projekts')), ('name', models.CharField(max_length=200, verbose_name='Name des Projekts')),
('description', models.TextField(max_length=500, verbose_name='Kurzbeschreibung des Projekts')), ('description', models.TextField(max_length=500, verbose_name='Kurzbeschreibung des Projekts')),
('categories', models.JSONField(default=list, verbose_name='Projektkategorie')), ('categories', models.JSONField(default=list, verbose_name='Projektkategorie')),
('categories_other', models.CharField(blank=True, max_length=200, null=True, verbose_name='Projektkategorie: Sonstiges (kurz)')), ('categories_other', models.CharField(blank=True, max_length=200, verbose_name='Projektkategorie: Sonstiges (kurz)')),
('wikimedia_projects', models.JSONField(default=list, verbose_name='Wikimedia Projekt(e)')), ('wikimedia_projects', models.JSONField(default=list, verbose_name='Wikimedia Projekt(e)')),
('wikimedia_other', models.CharField(blank=True, max_length=200, null=True, verbose_name='Wikimedia-Projekt: Anderes (kurz)')), ('wikimedia_other', models.CharField(blank=True, max_length=200, verbose_name='Wikimedia-Projekt: Anderes (kurz)')),
('start', models.DateField(verbose_name='Startdatum')), ('start', models.DateField(verbose_name='Startdatum')),
('end', models.DateField(verbose_name='Erwartetes Projektende')), ('end', models.DateField(verbose_name='Erwartetes Projektende')),
('participants_estimated', models.IntegerField(validators=[django.core.validators.MinValueValidator(0)], verbose_name='Teilnehmende angefragt')), ('participants_estimated', models.PositiveIntegerField(verbose_name='Zahl der Teilnehmenden')),
('page', models.URLField(blank=True, max_length=2000, null=True, verbose_name='Link zur Projektseite')), ('page', models.URLField(blank=True, max_length=2000, verbose_name='Link zur Projektseite')),
('group', models.CharField(blank=True, max_length=2000, null=True, verbose_name='Mitorganisierende')), ('group', models.CharField(blank=True, max_length=2000, verbose_name='Mitorganisierende')),
('location', models.CharField(blank=True, max_length=2000, null=True, verbose_name='Ort/Adresse/Location')), ('location', models.CharField(blank=True, max_length=2000, verbose_name='Ort')),
('cost', models.IntegerField(validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(1000)], verbose_name='Kosten (EUR, ganzzahlig)')), ('cost', models.PositiveIntegerField(validators=[validate_cost], verbose_name='Höhe der Projektkosten')),
('insurance', models.BooleanField(default=False, verbose_name='Haftpflichtversicherung gewünscht?')), ('insurance', models.BooleanField(default=False, verbose_name='Versicherung gewünscht?')),
('notes', models.TextField(blank=True, max_length=2000, null=True, verbose_name='Anmerkungen')), ('notes', models.TextField(blank=True, max_length=2000, verbose_name='Anmerkungen')),
('decision', models.CharField(choices=[('OPEN', 'offen'), ('APPROVED', 'bewilligt'), ('DECLINED', 'abgelehnt')], default='OPEN', max_length=10)), ('decision', models.CharField(choices=[('OPEN', 'offen'), ('APPROVED', 'bewilligt'), ('DECLINED', 'abgelehnt')], db_index=True, default='OPEN', max_length=10)),
('decision_date', models.DateField(blank=True, null=True)), ('decision_date', models.DateField(blank=True, db_index=True, null=True)),
('decided_by', models.CharField(blank=True, max_length=100, null=True, verbose_name='Entschieden von')), ('decided_by', models.CharField(blank=True, max_length=100, verbose_name='Entschieden von')),
], ],
options={ options={
'verbose_name': 'Projektförderungs-Antrag (< 1000 EUR)', 'verbose_name': 'Projektförderungs-Antrag (< 1000 EUR)',
'verbose_name_plural': 'Projects_requested', 'verbose_name_plural': 'Projects_requested',
'ordering': ('-id',),
}, },
), ),
migrations.CreateModel( migrations.CreateModel(
name='ProjectsDeclined', name='ProjectsDeclined',
fields=[ fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('original_request_id', models.IntegerField()), ('original_request_id', models.PositiveIntegerField()),
('name', models.CharField(max_length=200)), ('name', models.CharField(max_length=200)),
('realname', models.CharField(max_length=200)), ('realname', models.CharField(max_length=200)),
('email', models.EmailField(max_length=254)), ('email', models.EmailField(max_length=254)),
@ -59,6 +61,7 @@ class Migration(migrations.Migration):
], ],
options={ options={
'verbose_name_plural': 'Projects_declined', 'verbose_name_plural': 'Projects_declined',
'ordering': ('-decision_date', '-id'),
}, },
), ),
] ]

View File

@ -1,57 +0,0 @@
# Generated by Django 5.2.5 on 2025-09-28 22:27
import django.core.validators
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('input', '0100_projectrequest_projectsdeclined'),
]
operations = [
migrations.AlterModelOptions(
name='projectrequest',
options={'ordering': ('-id',), 'verbose_name': 'Projektförderungs-Antrag (< 1000 EUR)', 'verbose_name_plural': 'Projects_requested'},
),
migrations.AlterModelOptions(
name='projectsdeclined',
options={'ordering': ('-decision_date', '-id'), 'verbose_name_plural': 'Projects_declined'},
),
migrations.AlterField(
model_name='projectrequest',
name='cost',
field=models.IntegerField(validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(1000)], verbose_name='Höhe der Projektkosten'),
),
migrations.AlterField(
model_name='projectrequest',
name='decision',
field=models.CharField(choices=[('OPEN', 'offen'), ('APPROVED', 'bewilligt'), ('DECLINED', 'abgelehnt')], db_index=True, default='OPEN', max_length=10),
),
migrations.AlterField(
model_name='projectrequest',
name='decision_date',
field=models.DateField(blank=True, db_index=True, null=True),
),
migrations.AlterField(
model_name='projectrequest',
name='insurance',
field=models.BooleanField(default=False, verbose_name='Versicherung gewünscht?'),
),
migrations.AlterField(
model_name='projectrequest',
name='location',
field=models.CharField(blank=True, max_length=2000, null=True, verbose_name='Ort'),
),
migrations.AlterField(
model_name='projectrequest',
name='participants_estimated',
field=models.IntegerField(validators=[django.core.validators.MinValueValidator(0)], verbose_name='Zahl der Teilnehmenden'),
),
migrations.AlterField(
model_name='projectsdeclined',
name='original_request_id',
field=models.PositiveIntegerField(),
),
]

View File

@ -458,40 +458,42 @@ class Decision(models.TextChoices):
DECLINED = 'DECLINED', 'abgelehnt' DECLINED = 'DECLINED', 'abgelehnt'
validate_cost = MaxValueValidator(
limit_value=100,
message=(
'Bitte beachte, dass für Projektkosten über 1.000 EUR '
'ein öffentlicher Projektplan erforderlich ist '
'(siehe Wikipedia:Förderung/Projektplanung).'
),
)
# Application for project funding < 1000 EUR # Application for project funding < 1000 EUR
class ProjectRequest(Volunteer): class ProjectRequest(Volunteer):
name = models.CharField(max_length=200, verbose_name='Name des Projekts') name = models.CharField('Name des Projekts', max_length=200)
description = models.TextField(max_length=500, verbose_name='Kurzbeschreibung des Projekts') description = models.TextField('Kurzbeschreibung des Projekts', max_length=500)
# Multi-select: stored pragmatically as JSON # Multi-select: stored pragmatically as JSON
categories = models.JSONField(default=list, verbose_name='Projektkategorie') categories = models.JSONField('Projektkategorie', default=list)
categories_other = models.CharField(max_length=200, null=True, blank=True, categories_other = models.CharField('Projektkategorie: Sonstiges (kurz)', max_length=200, blank=True)
verbose_name='Projektkategorie: Sonstiges (kurz)') wikimedia_projects = models.JSONField('Wikimedia Projekt(e)', default=list)
wikimedia_projects = models.JSONField(default=list, verbose_name='Wikimedia Projekt(e)') wikimedia_other = models.CharField('Wikimedia-Projekt: Anderes (kurz)', max_length=200, blank=True)
wikimedia_other = models.CharField(max_length=200, null=True, blank=True,
verbose_name='Wikimedia-Projekt: Anderes (kurz)'
)
start = models.DateField('Startdatum') start = models.DateField('Startdatum')
end = models.DateField('Erwartetes Projektende') end = models.DateField('Erwartetes Projektende')
participants_estimated = models.IntegerField(verbose_name='Zahl der Teilnehmenden', participants_estimated = models.PositiveIntegerField('Zahl der Teilnehmenden')
validators=[MinValueValidator(0)])
page = models.URLField(max_length=2000, null=True, blank=True, verbose_name='Link zur Projektseite') page = models.URLField('Link zur Projektseite', max_length=2000, blank=True)
group = models.CharField(max_length=2000, null=True, blank=True, verbose_name='Mitorganisierende') group = models.CharField('Mitorganisierende', max_length=2000, blank=True)
location = models.CharField(max_length=2000, null=True, blank=True, verbose_name='Ort') location = models.CharField('Ort', max_length=2000, blank=True)
cost = models.IntegerField(verbose_name='Höhe der Projektkosten', cost = models.PositiveIntegerField('Höhe der Projektkosten', validators=[validate_cost])
validators=[MinValueValidator(0), MaxValueValidator(1000)]) insurance = models.BooleanField('Versicherung gewünscht?', default=False)
insurance = models.BooleanField(default=False, verbose_name='Versicherung gewünscht?') notes = models.TextField('Anmerkungen', max_length=2000, blank=True)
notes = models.TextField(max_length=2000, null=True, blank=True, verbose_name='Anmerkungen')
# Workflow fields (used only for the request process) # Workflow fields (used only for the request process)
decision = models.CharField( decision = models.CharField(max_length=10, choices=Decision.choices, default=Decision.OPEN, db_index=True)
max_length=10, choices=Decision.choices, default=Decision.OPEN, db_index=True
)
decision_date = models.DateField(null=True, blank=True, db_index=True) decision_date = models.DateField(null=True, blank=True, db_index=True)
decided_by = models.CharField(max_length=100, null=True, blank=True, verbose_name='Entschieden von') decided_by = models.CharField('Entschieden von', max_length=100, blank=True)
class Meta: class Meta:
verbose_name = 'Projektförderungs-Antrag (< 1000 EUR)' verbose_name = 'Projektförderungs-Antrag (< 1000 EUR)'
@ -502,16 +504,6 @@ class ProjectRequest(Volunteer):
return f'[Antrag] {self.name} ({self.realname})' return f'[Antrag] {self.name} ({self.realname})'
def clean(self): def clean(self):
super().clean()
# 1) Additional guard if MaxValueValidator is removed
if self.cost is not None and self.cost > 1000:
raise ValidationError({
'cost': ('Bitte beachte, dass für Projektkosten über 1.000 EUR '
'ein öffentlicher Projektplan erforderlich ist '
'(siehe Wikipedia:Förderung/Projektplanung).')
})
# 2) Required and allowed values # 2) Required and allowed values
if not self.categories: if not self.categories:
raise ValidationError({'categories': 'Bitte wähle mindestens eine Projektkategorie.'}) raise ValidationError({'categories': 'Bitte wähle mindestens eine Projektkategorie.'})