summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorÉtienne Loks <etienne.loks@iggdrasil.net>2021-04-28 09:55:25 +0200
committerÉtienne Loks <etienne.loks@iggdrasil.net>2021-07-29 11:54:14 +0200
commitd552436e1847fc7541d1abe643c5fed67109bae6 (patch)
tree8959fe77756ed8db59dcada5de146d1427030b9a
parentf5e7db4f7b3bceb0cdef84f9530e0cddc67beea8 (diff)
downloadIshtar-d552436e1847fc7541d1abe643c5fed67109bae6.tar.bz2
Ishtar-d552436e1847fc7541d1abe643c5fed67109bae6.zip
WIP: preventive forms
-rw-r--r--DEVELOP.md34
-rw-r--r--archaeological_files/forms.py188
-rw-r--r--archaeological_files/migrations/0106_auto_20210622_1456.py (renamed from archaeological_files/migrations/0106_auto_20210423_1206.py)175
-rw-r--r--archaeological_files/models.py65
-rw-r--r--archaeological_files/templates/ishtar/forms/preventive_detail.html2
-rw-r--r--archaeological_files/urls.py7
-rw-r--r--archaeological_files/views.py84
-rw-r--r--ishtar_common/templates/blocks/bs_formset_snippet.html26
-rw-r--r--ishtar_common/templates/ishtar/forms/base_form.html50
-rw-r--r--ishtar_common/templates/ishtar/forms/document.html51
-rw-r--r--ishtar_common/templates/widgets/GramKilogramWidget.html21
-rw-r--r--ishtar_common/templates/widgets/SquareMeterWidget.html21
-rw-r--r--ishtar_common/templates/widgets/UnitWidget.html (renamed from ishtar_common/templates/widgets/CentimeterMeterWidget.html)6
-rw-r--r--ishtar_common/templatetags/table_form.py9
-rw-r--r--ishtar_common/widgets.py17
15 files changed, 565 insertions, 191 deletions
diff --git a/DEVELOP.md b/DEVELOP.md
index f7398ba26..e91ca3ccf 100644
--- a/DEVELOP.md
+++ b/DEVELOP.md
@@ -3,11 +3,12 @@
Installation instruction for a debian buster with sudo.
### Install base source code
-```
+``` bash
sudo apt-get update
-sudo apt-get install git sed python3-pip libpq-dev python3-dev libjpeg-dev zlib1g-dev \
- libxml2-dev libxslt1-dev libgeos-dev python3-cairocffi tidy libtidy-dev binutils \
- libproj-dev gdal-bin libpangocairo-1.0-0 pandoc graphviz
+sudo apt-get install git sed python3-venv python3-wheel python3-pip libpq-dev \
+ python3-dev libjpeg-dev zlib1g-dev libxml2-dev libxslt1-dev libgeos-dev \
+ python3-cairocffi tidy libtidy-dev binutils libproj-dev gdal-bin \
+ libpangocairo-1.0-0 pandoc graphviz gettext
cd my_workspace
python3 -m venv ishtar-venv
@@ -19,18 +20,22 @@ pip3 install -r requirements.txt
### Install postgresql database
-```
-apt install postgresql postgresql-contrib postgresql-11-postgis-2.5 \
+``` bash
+sudo apt install postgresql postgresql-contrib postgresql-11-postgis-2.5 \
postgresql-11-postgis-2.5-scripts
sudo -u postgres psql
+```
+
+``` sql
CREATE DATABASE ishtar;
CREATE USER ishtar WITH ENCRYPTED PASSWORD 'mypassword';
GRANT ALL PRIVILEGES ON DATABASE ishtar TO ishtar;
+ALTER ROLE ishtar superuser;
```
### Configure and initialize your instance
-```
+``` bash
cd my_workspace
. ./ishtar-venv/bin/activate
cd ishtar
@@ -38,7 +43,22 @@ ISHTAR_PATH=`pwd`
cp example_project/local_settings.py.sample example_project/local_settings.py
editor example_project/local_settings.py
# edit settings: SECRET_KEY, DATABASE
+```
+
+``` python
+DATABASES = {
+ 'default': {
+ 'NAME': 'ishtar',
+ 'ENGINE': 'django.contrib.gis.db.backends.postgis',
+ 'HOST': '127.0.0.1',
+ 'PORT': '5432',
+ 'USER': 'ishtar',
+ 'PASSWORD': 'mypassword',
+ },
+}
+```
+``` bash
cd example_project
# collect static data
python3 ./manage.py collectstatic --noinput
diff --git a/archaeological_files/forms.py b/archaeological_files/forms.py
index 2cb08b5d7..51d1fdeb0 100644
--- a/archaeological_files/forms.py
+++ b/archaeological_files/forms.py
@@ -20,12 +20,13 @@
"""
Files forms definitions
"""
+
import datetime
from django import forms
from django.conf import settings
from django.core import validators
-from django.forms.formsets import formset_factory
+from django.forms.formsets import formset_factory, BaseFormSet
from django.utils.functional import lazy
from django.utils.safestring import mark_safe
from ishtar_common.utils import ugettext_lazy as _
@@ -57,6 +58,7 @@ from ishtar_common.forms import (
LockForm,
CustomFormSearch,
DocumentItemSelect,
+ FormHeader,
)
from ishtar_common.forms_common import get_town_field
from archaeological_operations.forms import (
@@ -506,6 +508,190 @@ class FinalFileDeleteForm(FinalForm):
confirm_end_msg = _("Would you like to delete this archaeological file ?")
+class FileFormPreventiveDetail(forms.ModelForm, CustomForm, ManageOldType):
+ form_label = _("Preventive file")
+ form_admin_name = _("Preventive file - 020 - Edition form")
+ form_slug = "preventive-020-edition-form"
+
+ associated_models = {}
+
+ HEADERS = {
+ "start_date": FormHeader(_("Dates")),
+ "total_developed_surface": FormHeader(_("Surfaces")),
+ }
+
+ class Meta:
+ model = models.File
+ fields = ["start_date", "end_date", "ground_start_date",
+ "ground_end_date", "study_period", "execution_report_date",
+ "total_developed_surface", "total_surface", "linear_meter"]
+
+ pk = forms.IntegerField(label="", required=False, widget=forms.HiddenInput)
+ start_date = forms.DateField(
+ label=_("Start date"), widget=DatePicker, required=False
+ )
+ end_date = forms.DateField(label=_("End date"), widget=DatePicker, required=False)
+ ground_start_date = forms.DateField(
+ label=_("Ground start date"), widget=DatePicker, required=False
+ )
+ ground_end_date = forms.DateField(
+ label=_("Ground end date"), widget=DatePicker, required=False
+ )
+ study_period = forms.CharField(
+ label=_("Study period"),
+ max_length=200,
+ required=False,
+ )
+ execution_report_date = forms.DateField(
+ label=_("Execution report date"), widget=DatePicker, required=False
+ )
+ total_developed_surface = forms.FloatField(
+ widget=widgets.AreaWidget,
+ label=_("Total developed surface (m2)"),
+ required=False,
+ validators=[
+ validators.MinValueValidator(0),
+ validators.MaxValueValidator(999999999),
+ ],
+ )
+ total_surface = forms.FloatField(
+ required=False,
+ widget=widgets.AreaWidget,
+ label=_("Total surface (m2)"),
+ validators=[
+ validators.MinValueValidator(0),
+ validators.MaxValueValidator(999999999),
+ ],
+ )
+ linear_meter = forms.IntegerField(
+ label=_("Linear meter (m)"), required=False,
+ widget=widgets.MeterKilometerWidget,
+ )
+
+ def __init__(self, *args, **kwargs):
+ self.user = None
+ if kwargs.get("user", None):
+ self.user = kwargs.pop("user")
+ super(FileFormPreventiveDetail, self).__init__(*args, **kwargs)
+
+
+class FileBaseFormset(forms.BaseModelFormSet):
+ def __init__(self, *args, **kwargs):
+ self.instance = None
+ if "instance" in kwargs:
+ self.instance = kwargs.pop("instance")
+ super().__init__(*args, **kwargs)
+ self.queryset = self.model.objects.filter(pk=None)
+ if self.instance:
+ self.queryset = self.model.objects.filter(file_id=self.instance.pk)
+
+
+class PreventiveFileJobForm(forms.ModelForm):
+ file_id = forms.IntegerField(widget=forms.HiddenInput)
+ job = forms.ChoiceField(choices=[])
+ man_by_day_planned = forms.FloatField(_("Man by day - planned"), required=False)
+ days_planned = forms.FloatField(_("Days - planned"), required=False)
+ man_by_day_worked = forms.FloatField(_("Man by day - worked"), required=False)
+ days_worked = forms.FloatField(_("Days - worked"), required=False)
+
+ class Meta:
+ model = models.PreventiveFileJob
+ exclude = ["file"]
+
+
+class PreventiveFileJobBaseFormSet(FileBaseFormset):
+ model = models.PreventiveFileJob
+
+
+PreventiveFileJobFormSet = formset_factory(
+ PreventiveFileJobForm, formset=PreventiveFileJobBaseFormSet, can_delete=True)
+PreventiveFileJobFormSet.form_label = _("Preventive - Jobs")
+PreventiveFileJobFormSet.form_admin_name = _("Preventive file - 030 - Jobs")
+PreventiveFileJobFormSet.form_slug = "preventive-030-jobs"
+
+
+class PreventiveFileGroundJobForm(forms.ModelForm):
+ file_id = forms.IntegerField(widget=forms.HiddenInput)
+ ground_job = forms.ChoiceField(choices=[])
+ man_by_day_planned = forms.FloatField(_("Man by day - planned"), required=False)
+ days_planned = forms.FloatField(_("Days - planned"), required=False)
+ man_by_day_worked = forms.FloatField(_("Man by day - worked"), required=False)
+ days_worked = forms.FloatField(_("Days - worked"), required=False)
+
+ class Meta:
+ model = models.PreventiveFileGroundJob
+ exclude = ["file"]
+
+
+class PreventiveFileGroundJobBaseFormSet(FileBaseFormset):
+ model = models.PreventiveFileGroundJob
+
+
+PreventiveFileGroundJobFormSet = formset_factory(
+ PreventiveFileGroundJobForm, formset=PreventiveFileGroundJobBaseFormSet,
+ can_delete=True
+)
+PreventiveFileGroundJobFormSet.form_label = _("Preventive - Ground jobs")
+PreventiveFileGroundJobFormSet.form_admin_name = _(
+ "Preventive file - 040 - Ground jobs"
+)
+PreventiveFileGroundJobFormSet.form_slug = "preventive-040-ground-jobs"
+
+
+class PreventiveFileEquipmentForm(forms.ModelForm):
+ file_id = forms.IntegerField(widget=forms.HiddenInput)
+ equipment = forms.ChoiceField(choices=[])
+ man_by_day_planned = forms.FloatField(_("Man by day - planned"), required=False)
+ days_planned = forms.FloatField(_("Days - planned"), required=False)
+ man_by_day_worked = forms.FloatField(_("Man by day - worked"), required=False)
+ days_worked = forms.FloatField(_("Days - worked"), required=False)
+
+ class Meta:
+ model = models.PreventiveFileEquipmentCost
+ exclude = ["file"]
+
+
+class PreventiveFileEquipmentBaseFormSet(FileBaseFormset):
+ model = models.PreventiveFileEquipmentCost
+
+
+PreventiveFileEquipmentFormSet = formset_factory(
+ PreventiveFileEquipmentForm, formset=PreventiveFileEquipmentBaseFormSet,
+ can_delete=True
+)
+PreventiveFileEquipmentFormSet.form_label = _("Preventive - Equipment")
+PreventiveFileEquipmentFormSet.form_admin_name = _("Preventive file - 050 - Equipments")
+PreventiveFileEquipmentFormSet.form_slug = "preventive-050-equipments"
+
+
+class PreventiveFileTechnicalServiceForm(forms.ModelForm):
+ file_id = forms.IntegerField(widget=forms.HiddenInput)
+ technical_service = forms.ChoiceField(choices=[])
+ man_by_day_planned = forms.FloatField(_("Man by day - planned"), required=False)
+ days_planned = forms.FloatField(_("Days - planned"), required=False)
+ man_by_day_worked = forms.FloatField(_("Man by day - worked"), required=False)
+ days_worked = forms.FloatField(_("Days - worked"), required=False)
+
+ class Meta:
+ model = models.PreventiveFileTechnicalServiceCost
+ exclude = ["file"]
+
+
+class PreventiveFileTechnicalServiceBaseFormSet(FileBaseFormset):
+ model = models.PreventiveFileTechnicalServiceCost
+
+
+PreventiveFileTechnicalServiceFormSet = formset_factory(
+ PreventiveFileTechnicalServiceForm,
+ formset=PreventiveFileTechnicalServiceBaseFormSet, can_delete=True
+)
+PreventiveFileTechnicalServiceFormSet.form_label = _("Preventive - Technical Services")
+PreventiveFileTechnicalServiceFormSet.form_admin_name = _(
+ "Preventive file - 060 - TechnicalServices"
+)
+PreventiveFileTechnicalServiceFormSet.form_slug = "preventive-060-technical_services"
+
+
class AdministrativeActFileModifySelect(TableSelect):
_model = AdministrativeAct
diff --git a/archaeological_files/migrations/0106_auto_20210423_1206.py b/archaeological_files/migrations/0106_auto_20210622_1456.py
index 5d147781e..2667a705d 100644
--- a/archaeological_files/migrations/0106_auto_20210423_1206.py
+++ b/archaeological_files/migrations/0106_auto_20210622_1456.py
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# Generated by Django 1.11.27 on 2021-04-23 12:06
+# Generated by Django 1.11.28 on 2021-06-22 14:56
from __future__ import unicode_literals
import django.core.validators
@@ -88,62 +88,14 @@ class Migration(migrations.Migration):
bases=(ishtar_common.models_common.Cached, models.Model),
),
migrations.CreateModel(
- name='PreventiveFile',
+ name='ManDays',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ('study_period', models.CharField(blank=True, default='', max_length=200, verbose_name='Study period')),
- ('start_date', models.DateField(blank=True, null=True, verbose_name='Start date')),
- ('end_date', models.DateField(blank=True, null=True, verbose_name='End date')),
- ('ground_start_date', models.DateField(blank=True, null=True, verbose_name='Ground start date')),
- ('ground_end_date', models.DateField(blank=True, null=True, verbose_name='Ground end date')),
- ('execution_report_date', models.DateField(blank=True, null=True, verbose_name='Execution report date')),
- ('linear_meter', models.IntegerField(blank=True, null=True, verbose_name='Linear meter')),
- ('file', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='archaeological_files.File', verbose_name='File')),
+ ('man_by_day_planned', models.FloatField(blank=True, null=True, verbose_name='Man by day - planned')),
+ ('days_planned', models.FloatField(blank=True, null=True, verbose_name='Days - planned')),
+ ('man_by_day_worked', models.FloatField(blank=True, null=True, verbose_name='Man by day - worked')),
+ ('days_worked', models.FloatField(blank=True, null=True, verbose_name='Days - worked')),
],
- options={
- 'verbose_name': 'Preventive file',
- 'verbose_name_plural': 'Preventive files',
- },
- ),
- migrations.CreateModel(
- name='PreventiveFileEquipmentCost',
- fields=[
- ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ('man_days_planned', models.FloatField(blank=True, null=True, verbose_name='Man-day planned')),
- ('man_days_worked', models.FloatField(blank=True, null=True, verbose_name='Man-day worked')),
- ('equipment_cost', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='archaeological_files.EquipmentCost')),
- ('file', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='archaeological_files.PreventiveFile')),
- ],
- options={
- 'abstract': False,
- },
- ),
- migrations.CreateModel(
- name='PreventiveFileJob',
- fields=[
- ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ('man_days_planned', models.FloatField(blank=True, null=True, verbose_name='Man-day planned')),
- ('man_days_worked', models.FloatField(blank=True, null=True, verbose_name='Man-day worked')),
- ('ground_man_days_planned', models.FloatField(blank=True, null=True, verbose_name='Ground man-day planned')),
- ('ground_man_days_worked', models.FloatField(blank=True, null=True, verbose_name='Ground man-day worked')),
- ('file', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='archaeological_files.PreventiveFile')),
- ('job', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='archaeological_files.Job')),
- ],
- options={
- 'abstract': False,
- },
- ),
- migrations.CreateModel(
- name='PreventiveFileTechnicalServiceCost',
- fields=[
- ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ('man_days_planned', models.FloatField(blank=True, null=True, verbose_name='Man-day planned')),
- ('man_days_worked', models.FloatField(blank=True, null=True, verbose_name='Man-day worked')),
- ('file', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='archaeological_files.PreventiveFile')),
- ],
- options={
- 'abstract': False,
- },
),
migrations.CreateModel(
name='TechnicalService',
@@ -177,9 +129,108 @@ class Migration(migrations.Migration):
},
),
migrations.AddField(
- model_name='preventivefiletechnicalservicecost',
- name='technical_service_cost',
- field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='archaeological_files.TechnicalServiceCost'),
+ model_name='file',
+ name='execution_report_date',
+ field=models.DateField(blank=True, null=True, verbose_name='Execution report date'),
+ ),
+ migrations.AddField(
+ model_name='file',
+ name='ground_end_date',
+ field=models.DateField(blank=True, null=True, verbose_name='Ground end date'),
+ ),
+ migrations.AddField(
+ model_name='file',
+ name='ground_start_date',
+ field=models.DateField(blank=True, null=True, verbose_name='Ground start date'),
+ ),
+ migrations.AddField(
+ model_name='file',
+ name='linear_meter',
+ field=models.IntegerField(blank=True, null=True, verbose_name='Linear meter'),
+ ),
+ migrations.AddField(
+ model_name='file',
+ name='start_date',
+ field=models.DateField(blank=True, null=True, verbose_name='Start date'),
+ ),
+ migrations.AddField(
+ model_name='file',
+ name='study_period',
+ field=models.CharField(blank=True, default='', max_length=200, verbose_name='Study period'),
+ ),
+ migrations.AddField(
+ model_name='historicalfile',
+ name='execution_report_date',
+ field=models.DateField(blank=True, null=True, verbose_name='Execution report date'),
+ ),
+ migrations.AddField(
+ model_name='historicalfile',
+ name='ground_end_date',
+ field=models.DateField(blank=True, null=True, verbose_name='Ground end date'),
+ ),
+ migrations.AddField(
+ model_name='historicalfile',
+ name='ground_start_date',
+ field=models.DateField(blank=True, null=True, verbose_name='Ground start date'),
+ ),
+ migrations.AddField(
+ model_name='historicalfile',
+ name='linear_meter',
+ field=models.IntegerField(blank=True, null=True, verbose_name='Linear meter'),
+ ),
+ migrations.AddField(
+ model_name='historicalfile',
+ name='start_date',
+ field=models.DateField(blank=True, null=True, verbose_name='Start date'),
+ ),
+ migrations.AddField(
+ model_name='historicalfile',
+ name='study_period',
+ field=models.CharField(blank=True, default='', max_length=200, verbose_name='Study period'),
+ ),
+ migrations.AlterField(
+ model_name='file',
+ name='end_date',
+ field=models.DateField(blank=True, null=True, verbose_name='End date'),
+ ),
+ migrations.AlterField(
+ model_name='historicalfile',
+ name='end_date',
+ field=models.DateField(blank=True, null=True, verbose_name='End date'),
+ ),
+ migrations.CreateModel(
+ name='PreventiveFileEquipmentCost',
+ fields=[
+ ('mandays_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='archaeological_files.ManDays')),
+ ],
+ bases=('archaeological_files.mandays',),
+ ),
+ migrations.CreateModel(
+ name='PreventiveFileGroundJob',
+ fields=[
+ ('mandays_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='archaeological_files.ManDays')),
+ ('file', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='ground_jobs', to='archaeological_files.File')),
+ ('job', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='archaeological_files.Job')),
+ ],
+ bases=('archaeological_files.mandays',),
+ ),
+ migrations.CreateModel(
+ name='PreventiveFileJob',
+ fields=[
+ ('mandays_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='archaeological_files.ManDays')),
+ ('file', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='jobs', to='archaeological_files.File')),
+ ('job', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='archaeological_files.Job')),
+ ],
+ bases=('archaeological_files.mandays',),
+ ),
+ migrations.CreateModel(
+ name='PreventiveFileTechnicalServiceCost',
+ fields=[
+ ('mandays_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='archaeological_files.ManDays')),
+ ('file', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='technical_service_costs', to='archaeological_files.File')),
+ ('technical_service_cost', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='archaeological_files.TechnicalServiceCost')),
+ ],
+ bases=('archaeological_files.mandays',),
),
migrations.AddField(
model_name='equipmenttype',
@@ -191,4 +242,14 @@ class Migration(migrations.Migration):
name='equipment_type',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='archaeological_files.EquipmentType', verbose_name='Equipment'),
),
+ migrations.AddField(
+ model_name='preventivefileequipmentcost',
+ name='equipment_cost',
+ field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='archaeological_files.EquipmentCost'),
+ ),
+ migrations.AddField(
+ model_name='preventivefileequipmentcost',
+ name='file',
+ field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='equipment_costs', to='archaeological_files.File'),
+ ),
]
diff --git a/archaeological_files/models.py b/archaeological_files/models.py
index db46c1ca7..fde527ae1 100644
--- a/archaeological_files/models.py
+++ b/archaeological_files/models.py
@@ -521,6 +521,18 @@ class File(
)
# <-- research archaeology
+ # --> preventive detail
+ study_period = models.CharField(_("Study period"), max_length=200,
+ default="", blank=True)
+ start_date = models.DateField(_("Start date"), blank=True, null=True)
+ end_date = models.DateField(_("End date"), blank=True, null=True)
+ ground_start_date = models.DateField(_("Ground start date"), blank=True, null=True)
+ ground_end_date = models.DateField(_("Ground end date"), blank=True, null=True)
+ execution_report_date = models.DateField(_("Execution report date"), blank=True,
+ null=True)
+ linear_meter = models.IntegerField(_("Linear meter"), blank=True, null=True)
+ # <-- preventive detail
+
documents = models.ManyToManyField(
Document, related_name="files", verbose_name=_("Documents"), blank=True
)
@@ -1142,47 +1154,44 @@ class FileDashboard:
)
-class PreventiveFile(models.Model):
- file = models.ForeignKey(File, verbose_name=_("File"))
- study_period = models.CharField(_("Study period"), max_length=200,
- default="", blank=True)
- start_date = models.DateField(_("Start date"), blank=True, null=True)
- end_date = models.DateField(_("End date"), blank=True, null=True)
- ground_start_date = models.DateField(_("Ground start date"), blank=True, null=True)
- ground_end_date = models.DateField(_("Ground end date"), blank=True, null=True)
- execution_report_date = models.DateField(_("Execution report date"), blank=True,
- null=True)
- linear_meter = models.IntegerField(_("Linear meter"), blank=True, null=True)
+class ManDays(models.Model):
+ man_by_day_planned = models.FloatField(
+ _("Man by day - planned"), null=True, blank=True)
+ days_planned = models.FloatField(
+ _("Days - planned"), null=True, blank=True)
+ man_by_day_worked = models.FloatField(
+ _("Man by day - worked"), null=True, blank=True)
+ days_worked = models.FloatField(
+ _("Days - worked"), null=True, blank=True)
- class Meta:
- verbose_name = _("Preventive file")
- verbose_name_plural = _("Preventive files")
+ @property
+ def man_days_planned(self):
+ if not self.days_planned or not self.man_by_day_planned:
+ return 0
+ return self.days_planned * self.man_by_day_planned
+ @property
+ def man_days_worked(self):
+ if not self.days_worked or not self.man_by_day_worked:
+ return 0
+ return self.days_worked * self.man_by_day_worked
-class ManDays(models.Model):
- man_days_planned = models.FloatField(
- _("Man-day planned"), null=True, blank=True)
- man_days_worked = models.FloatField(
- _("Man-day worked"), null=True, blank=True)
- class Meta:
- abstract = True
+class PreventiveFileGroundJob(ManDays):
+ file = models.ForeignKey(File, related_name="ground_jobs")
+ job = models.ForeignKey(Job)
class PreventiveFileJob(ManDays):
- file = models.ForeignKey(PreventiveFile)
+ file = models.ForeignKey(File, related_name="jobs")
job = models.ForeignKey(Job)
- ground_man_days_planned = models.FloatField(
- _("Ground man-day planned"), null=True, blank=True)
- ground_man_days_worked = models.FloatField(
- _("Ground man-day worked"), null=True, blank=True)
class PreventiveFileEquipmentCost(ManDays):
- file = models.ForeignKey(PreventiveFile)
+ file = models.ForeignKey(File, related_name="equipment_costs")
equipment_cost = models.ForeignKey(EquipmentCost)
class PreventiveFileTechnicalServiceCost(ManDays):
- file = models.ForeignKey(PreventiveFile)
+ file = models.ForeignKey(File, related_name="technical_service_costs")
technical_service_cost = models.ForeignKey(TechnicalServiceCost)
diff --git a/archaeological_files/templates/ishtar/forms/preventive_detail.html b/archaeological_files/templates/ishtar/forms/preventive_detail.html
new file mode 100644
index 000000000..3cdfb3da0
--- /dev/null
+++ b/archaeological_files/templates/ishtar/forms/preventive_detail.html
@@ -0,0 +1,2 @@
+{% extends "ishtar/forms/base_form.html" %}
+{% load i18n %}
diff --git a/archaeological_files/urls.py b/archaeological_files/urls.py
index b245b0aea..3fcf9a42c 100644
--- a/archaeological_files/urls.py
+++ b/archaeological_files/urls.py
@@ -137,4 +137,11 @@ urlpatterns = [
check_rights(["add_operation"])(views.file_add_operation),
name="file-add-operation",
),
+ url(
+ r'^file/edit-preventive/(?P<pk>.+)/$',
+ check_rights(["change_file", "change_own_file"])(
+ views.PreventiveEditView.as_view()
+ ),
+ name="file-edit-preventive",
+ )
]
diff --git a/archaeological_files/views.py b/archaeological_files/views.py
index 35bd9d7c8..a927a94e6 100644
--- a/archaeological_files/views.py
+++ b/archaeological_files/views.py
@@ -23,11 +23,12 @@ import re
from django.core.urlresolvers import reverse
from django.db.models import Q
from django.http import HttpResponse, Http404, HttpResponseRedirect
+from django.views.generic.edit import UpdateView
from django.shortcuts import redirect, render
from ishtar_common.utils import ugettext_lazy as _
from ishtar_common.views import wizard_is_available
-from ishtar_common.views_item import get_item, show_item, revert_item
+from ishtar_common.views_item import get_item, show_item, revert_item, check_permission
from archaeological_operations.wizards import (
AdministrativeActDeletionWizard,
@@ -45,6 +46,7 @@ from archaeological_files.wizards import (
FileAdministrativeActWizard,
FileEditAdministrativeActWizard,
)
+from ishtar_common.views import IshtarMixin, LoginRequiredMixin
from archaeological_operations.wizards import OperationWizard
from archaeological_operations.views import operation_creation_wizard
@@ -354,3 +356,83 @@ def reset_wizards(request):
(AdministrativeActDeletionWizard, "file_administrativeactfile_deletion_wizard"),
):
wizard_class.session_reset(request, url_name)
+
+
+class MixFormFormsetUpdateView(UpdateView):
+ form_inlines_class = []
+
+ def get(self, request, *args, **kwargs):
+ self.object = self.get_object()
+ self.inline_forms = [
+ inline(instance=self.object) for inline in self.form_inlines_class
+ ]
+ return super(MixFormFormsetUpdateView, self).get(request, *args, **kwargs)
+
+ def post(self, request, *args, **kwargs):
+ self.object = self.get_object()
+ form = self.form_class(data=request.POST, instance=self.object)
+ inline_forms = [
+ inline(instance=self.object, data=request.POST)
+ for inline in self.form_inlines_class
+ ]
+ if form.is_valid() and all((inline.is_valid() for inline in inline_forms)):
+ return self.form_valid(form, inline_forms)
+ else:
+ return self.form_invalid(form, inline_forms)
+
+ def form_valid(self, form, inline_forms):
+ self.object = form.save()
+ for inline in inline_forms:
+ # save inlines...
+ pass
+ return HttpResponseRedirect(self.get_success_url())
+
+ def form_invalid(self, form, inline_forms):
+ return self.render_to_response(
+ self.get_context_data(form=form, inlines=inline_forms)
+ )
+
+ def get_context_data(self, *args, **kwargs):
+ data = super(MixFormFormsetUpdateView, self).get_context_data(*args, **kwargs)
+ data["inline_forms"] = self.inline_forms
+ return data
+
+
+class PreventiveEditView(IshtarMixin, LoginRequiredMixin, MixFormFormsetUpdateView):
+ page_name = _("Preventive modification")
+ form_class = forms.FileFormPreventiveDetail
+ form_inlines_class = [
+ forms.PreventiveFileJobFormSet,
+ forms.PreventiveFileGroundJobFormSet,
+ forms.PreventiveFileEquipmentFormSet,
+ forms.PreventiveFileTechnicalServiceFormSet
+ ]
+ template_name = "ishtar/forms/preventive_detail.html"
+ model = models.File
+
+
+ def get_success_url(self):
+ return reverse("file_modification") + "?open_item={}".format(self.object.pk)
+
+ def get_form_kwargs(self):
+ kwargs = super(PreventiveEditView, self).get_form_kwargs()
+ try:
+ file = models.File.objects.get(pk=self.kwargs.get("pk"))
+ except models.Document.DoesNotExist:
+ raise Http404()
+ if not check_permission(self.request, "file/edit-preventive/", file.pk):
+ raise Http404()
+ initial = {}
+ for k in (
+ list(self.form_class.base_fields.keys())
+ ):
+ value = getattr(file, k)
+ if hasattr(value, "all"):
+ value = ",".join([str(v.pk) for v in value.all()])
+ if hasattr(value, "pk"):
+ value = value.pk
+ initial[k] = value
+ kwargs["initial"] = initial
+ kwargs["user"] = self.request.user
+ self.file = file
+ return kwargs \ No newline at end of file
diff --git a/ishtar_common/templates/blocks/bs_formset_snippet.html b/ishtar_common/templates/blocks/bs_formset_snippet.html
new file mode 100644
index 000000000..0d089ee1b
--- /dev/null
+++ b/ishtar_common/templates/blocks/bs_formset_snippet.html
@@ -0,0 +1,26 @@
+{% load i18n from_dict %}
+<div class="form-row">
+ {% for form in formset %}
+ {% if form.non_field_errors and not no_error %}
+ <div class="alert alert-danger" role="alert">
+ {{form.non_field_errors}}
+ </div>
+ {% endif %}
+
+ {% for hidden in form.hidden_fields %}
+ {{hidden}}
+ {% if hidden.errors %}<div class="invalid-feedback">
+ {{ hidden.errors }}
+ </div>{% endif %}
+ {% endfor %}
+
+ {% csrf_token %}
+ {% for field in form.visible_fields %}
+ {% include "blocks/bs_field_snippet.html" %}
+ {% endfor %}
+
+ {% if form.extra_render %}
+ {{form.extra_render|safe}}
+ {% endif %}
+ {% endfor %}
+</div>
diff --git a/ishtar_common/templates/ishtar/forms/base_form.html b/ishtar_common/templates/ishtar/forms/base_form.html
new file mode 100644
index 000000000..fb1fc997f
--- /dev/null
+++ b/ishtar_common/templates/ishtar/forms/base_form.html
@@ -0,0 +1,50 @@
+{% extends "base.html" %}
+{% load i18n inline_formset table_form from_dict %}
+{% block extra_head %}
+{{form.media}}
+{% endblock %}
+
+{% block pre_container %}
+<form enctype="multipart/form-data" action="." method="post"{% if confirm %}
+ onsubmit='return confirm("{{confirm}}");'{% endif %}>{% csrf_token %}
+{% endblock %}
+{% block content %}
+<h2>{{page_name}}</h2>
+<div class='form{% if not form.SEARCH_AND_SELECT %} container{% endif %}'>
+ {% if form.non_field_errors or form.errors %}
+ <div class="alert alert-danger">
+ <div><i class="fa fa-exclamation-triangle" aria-hidden="true"></i>
+ {% trans "Error on validation. Check all your fields. Modification not saved." %}
+ </div>
+ {% for key, error_details in form.errors.items %}
+ {% with field=key|from_dict:form.fields %}<strong>{{ field.label }}</strong>{{error_details}}{% endwith %}
+ {% endfor %}
+ </div>
+ {% endif %}
+ {% block form_head %}
+ {% endblock %}
+ {% bs_form form 0 True %}
+ {% for inline in inline_forms %}
+ {% bs_formset inline 0 True %}
+ {% endfor %}
+</div>
+{% endblock %}
+
+{% block footer %}
+<div id="footer">
+ {% if form.SEARCH_AND_SELECT %}
+ <p class="confirm-message">{% trans "Search and select an item in the table" %}</p>
+ {% endif %}
+ <div id='validation-bar' class="row text-center">
+ <div class="col-sm">
+ <button type="submit" id="submit_form" name='validate'
+ value="validate" class="btn btn-success">
+ {% if submit_label %}{{submit_label}}{% else%}{% trans "Validate" %}{% endif %}
+ </button>
+ </div>
+ </div>
+ {% include 'ishtar/blocks/footer.html' %}
+</div>
+</form>
+{% endblock %}
+
diff --git a/ishtar_common/templates/ishtar/forms/document.html b/ishtar_common/templates/ishtar/forms/document.html
index 8e2683a03..fe3df8c74 100644
--- a/ishtar_common/templates/ishtar/forms/document.html
+++ b/ishtar_common/templates/ishtar/forms/document.html
@@ -1,48 +1,7 @@
-{% extends "base.html" %}
-{% load i18n inline_formset table_form from_dict %}
-{% block extra_head %}
-{{form.media}}
-{% endblock %}
-
-{% block pre_container %}
-<form enctype="multipart/form-data" action="." method="post"{% if confirm %}
- onsubmit='return confirm("{{confirm}}");'{% endif %}>{% csrf_token %}
-{% endblock %}
-{% block content %}
-<h2>{{page_name}}</h2>
-<div class='form{% if not form.SEARCH_AND_SELECT %} container{% endif %}'>
- {% if form.non_field_errors or form.errors %}
- <div class="alert alert-danger">
- <div><i class="fa fa-exclamation-triangle" aria-hidden="true"></i>
- {% trans "Error on validation. Check all your fields. Modification not saved." %}
- </div>
- {% for key, error_details in form.errors.items %}
- {% with field=key|from_dict:form.fields %}<strong>{{ field.label }}</strong>{{error_details}}{% endwith %}
- {% endfor %}
- </div>
- {% endif %}
- {% if item_related_label %}<div class="alert alert-info">
+{% extends "ishtar/forms/base_form.html" %}
+{% load i18n %}
+{% block form_head %}
+{% if item_related_label %}<div class="alert alert-info">
<strong>{% trans "Related items" %}{% trans ":" %}</strong> {{ item_related_label }}
- </div>{% endif %}
- {% bs_form form 0 True %}
-</div>
-{% endblock %}
-
-{% block footer %}
-<div id="footer">
- {% if form.SEARCH_AND_SELECT %}
- <p class="confirm-message">{% trans "Search and select an item in the table" %}</p>
- {% endif %}
- <div id='validation-bar' class="row text-center">
- <div class="col-sm">
- <button type="submit" id="submit_form" name='validate'
- value="validate" class="btn btn-success">
- {% if submit_label %}{{submit_label}}{% else%}{% trans "Validate" %}{% endif %}
- </button>
- </div>
- </div>
- {% include 'ishtar/blocks/footer.html' %}
-</div>
-</form>
+</div>{% endif %}
{% endblock %}
-
diff --git a/ishtar_common/templates/widgets/GramKilogramWidget.html b/ishtar_common/templates/widgets/GramKilogramWidget.html
deleted file mode 100644
index 27c066d13..000000000
--- a/ishtar_common/templates/widgets/GramKilogramWidget.html
+++ /dev/null
@@ -1,21 +0,0 @@
-<div class="input-group">
- <input class="area_widget form-control" type="text"{{final_attrs|safe}}>
- <div class="input-group-append">
- <div class="input-group-text">
- {{unit}} (<span id="kg_{{id}}">0</span>&nbsp;kg)
- </div>
- </div>
-</div>
-<script type="text/javascript"><!--//
- function evaluate_{{safe_id}}(){
- value = parseFloat($("#{{id}}").val());
- if(!isNaN(value)){
- value = value/1000;
- } else {
- value = 0;
- }
- $("#kg_{{id}}").html(value);
- }
- $("#{{id}}").keyup(evaluate_{{safe_id}});
- $(document).ready(evaluate_{{safe_id}}());
-//--></script>
diff --git a/ishtar_common/templates/widgets/SquareMeterWidget.html b/ishtar_common/templates/widgets/SquareMeterWidget.html
deleted file mode 100644
index 09e93b425..000000000
--- a/ishtar_common/templates/widgets/SquareMeterWidget.html
+++ /dev/null
@@ -1,21 +0,0 @@
-<div class="input-group">
- <input class="area_widget form-control" type="text"{{final_attrs|safe}}>
- <div class="input-group-append">
- <div class="input-group-text">
- {{unit}} (<span id="ha_{{id}}">0</span>&nbsp;ha)
- </div>
- </div>
-</div>
-<script type="text/javascript"><!--//
- function evaluate_{{safe_id}}(){
- value = parseFloat($("#{{id}}").val());
- if(!isNaN(value)){
- value = value/10000;
- } else {
- value = 0;
- }
- $("#ha_{{id}}").html(value);
- }
- $("#{{id}}").keyup(evaluate_{{safe_id}});
- $(document).ready(evaluate_{{safe_id}}());
-//--></script>
diff --git a/ishtar_common/templates/widgets/CentimeterMeterWidget.html b/ishtar_common/templates/widgets/UnitWidget.html
index 00c1614b5..46b883352 100644
--- a/ishtar_common/templates/widgets/CentimeterMeterWidget.html
+++ b/ishtar_common/templates/widgets/UnitWidget.html
@@ -2,7 +2,7 @@
<input class="area_widget form-control" type="text"{{final_attrs|safe}}>
<div class="input-group-append">
<div class="input-group-text">
- {{unit}} (<span id="meter_{{id}}">0</span>&nbsp;m)
+ {{unit1}} (<span id="subunit_{{id}}">0</span>&nbsp;{{unit2}})
</div>
</div>
</div>
@@ -10,11 +10,11 @@
function evaluate_{{safe_id}}(){
value = parseFloat($("#{{id}}").val());
if(!isNaN(value)){
- value = value/100;
+ value = value/{{factor}};
} else {
value = 0;
}
- $("#meter_{{id}}").html(value);
+ $("#subunit_{{id}}").html(value);
}
$("#{{id}}").keyup(evaluate_{{safe_id}});
$(document).ready(evaluate_{{safe_id}}());
diff --git a/ishtar_common/templatetags/table_form.py b/ishtar_common/templatetags/table_form.py
index d5b20da26..819d99c50 100644
--- a/ishtar_common/templatetags/table_form.py
+++ b/ishtar_common/templatetags/table_form.py
@@ -16,6 +16,15 @@ def bs_form(context, form, position=0, no_error=False):
'no_error': no_error}
+@register.inclusion_tag('blocks/bs_formset_snippet.html', takes_context=True)
+def bs_formset(context, formset, position=0, no_error=False):
+ user = context['user']
+ show_field_number = user.ishtaruser and user.ishtaruser.show_field_number()
+ return {'formset': formset, 'odd': position % 2,
+ 'show_field_number': show_field_number,
+ 'no_error': no_error}
+
+
@register.inclusion_tag('blocks/bs_compact_form_snippet.html',
takes_context=True)
def bs_compact_form(context, form, position=0):
diff --git a/ishtar_common/widgets.py b/ishtar_common/widgets.py
index 8f95f4521..008925276 100644
--- a/ishtar_common/widgets.py
+++ b/ishtar_common/widgets.py
@@ -521,18 +521,23 @@ class CustomWidget(forms.TextInput):
class SquareMeterWidget(CustomWidget):
- TEMPLATE = "widgets/SquareMeterWidget.html"
- EXTRA_DCT = {"unit": settings.SURFACE_UNIT_LABEL}
+ TEMPLATE = "widgets/UnitWidget.html"
+ EXTRA_DCT = {"unit1": "m²", "unit2": "ha", "factor": 10000}
class GramKilogramWidget(CustomWidget):
- TEMPLATE = "widgets/GramKilogramWidget.html"
- EXTRA_DCT = {"unit": "g"}
+ TEMPLATE = "widgets/UnitWidget.html"
+ EXTRA_DCT = {"unit1": "g", "unit2": "kg", "factor": 1000}
class CentimeterMeterWidget(CustomWidget):
- TEMPLATE = "widgets/CentimeterMeterWidget.html"
- EXTRA_DCT = {"unit": "cm"}
+ TEMPLATE = "widgets/UnitWidget.html"
+ EXTRA_DCT = {"unit1": "cm", "unit2": "m", "factor": 100}
+
+
+class MeterKilometerWidget(CustomWidget):
+ TEMPLATE = "widgets/UnitWidget.html"
+ EXTRA_DCT = {"unit1": "m", "unit2": "km", "factor": 1000}
AreaWidget = forms.TextInput