Compare commits
No commits in common. "cd596ce03ba3e9f5dd6aa94e628051021ae64759" and "f10af784e0d04350582f0337e81babe2b69f452a" have entirely different histories.
cd596ce03b
...
f10af784e0
@ -104,9 +104,9 @@ class TimespanReport:
|
|||||||
"iterable": self.evaluated_laws,
|
"iterable": self.evaluated_laws,
|
||||||
"attrs": [
|
"attrs": [
|
||||||
"short_name",
|
"short_name",
|
||||||
"num",
|
|
||||||
"num_checked",
|
"num_checked",
|
||||||
"num_recorded",
|
"num_recorded",
|
||||||
|
"num",
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"i_laws_checked": self.law_sum_checked,
|
"i_laws_checked": self.law_sum_checked,
|
||||||
|
@ -64,7 +64,6 @@ class CompensationAPISerializerV1(AbstractModelAPISerializerV1, AbstractCompensa
|
|||||||
obj = Compensation()
|
obj = Compensation()
|
||||||
created = create_action
|
created = create_action
|
||||||
obj.created = created
|
obj.created = created
|
||||||
obj.modified = created
|
|
||||||
obj.geometry = geometry
|
obj.geometry = geometry
|
||||||
return obj
|
return obj
|
||||||
|
|
||||||
|
@ -103,7 +103,6 @@ class EcoAccountAPISerializerV1(AbstractModelAPISerializerV1,
|
|||||||
obj.legal = Legal()
|
obj.legal = Legal()
|
||||||
created = create_action
|
created = create_action
|
||||||
obj.created = created
|
obj.created = created
|
||||||
obj.modified = created
|
|
||||||
obj.geometry = geometry
|
obj.geometry = geometry
|
||||||
return obj
|
return obj
|
||||||
|
|
||||||
|
@ -85,7 +85,6 @@ class EmaAPISerializerV1(AbstractModelAPISerializerV1, AbstractCompensationAPISe
|
|||||||
)
|
)
|
||||||
created = create_action
|
created = create_action
|
||||||
obj.created = created
|
obj.created = created
|
||||||
obj.modified = created
|
|
||||||
obj.geometry = geometry
|
obj.geometry = geometry
|
||||||
return obj
|
return obj
|
||||||
|
|
||||||
|
@ -76,7 +76,6 @@ class InterventionAPISerializerV1(AbstractModelAPISerializerV1,
|
|||||||
created = create_action
|
created = create_action
|
||||||
obj.legal = legal
|
obj.legal = legal
|
||||||
obj.created = created
|
obj.created = created
|
||||||
obj.modified = created
|
|
||||||
obj.geometry = geometry
|
obj.geometry = geometry
|
||||||
obj.responsible = resp
|
obj.responsible = resp
|
||||||
return obj
|
return obj
|
||||||
|
@ -156,7 +156,6 @@ class NewCompensationForm(AbstractCompensationForm,
|
|||||||
title=title,
|
title=title,
|
||||||
intervention=intervention,
|
intervention=intervention,
|
||||||
created=action,
|
created=action,
|
||||||
modified=action,
|
|
||||||
is_cef=is_cef,
|
is_cef=is_cef,
|
||||||
is_coherence_keeping=is_coherence_keeping,
|
is_coherence_keeping=is_coherence_keeping,
|
||||||
is_pik=is_pik,
|
is_pik=is_pik,
|
||||||
|
@ -117,7 +117,6 @@ class NewEcoAccountForm(AbstractCompensationForm, CompensationResponsibleFormMix
|
|||||||
responsible=responsible,
|
responsible=responsible,
|
||||||
deductable_surface=surface,
|
deductable_surface=surface,
|
||||||
created=action,
|
created=action,
|
||||||
modified=action,
|
|
||||||
comment=comment,
|
comment=comment,
|
||||||
is_pik=is_pik,
|
is_pik=is_pik,
|
||||||
legal=legal
|
legal=legal
|
||||||
|
@ -16,6 +16,10 @@
|
|||||||
<th scope="row">{% trans 'Conservation office' %}</th>
|
<th scope="row">{% trans 'Conservation office' %}</th>
|
||||||
<td class="align-middle">{{obj.responsible.conservation_office.str_as_office|default_if_none:""}}</td>
|
<td class="align-middle">{{obj.responsible.conservation_office.str_as_office|default_if_none:""}}</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th scope="row">{% trans 'Conservation office file number' %}</th>
|
||||||
|
<td class="align-middle">{{obj.responsible.conservation_file_number|default_if_none:""}}</td>
|
||||||
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row">{% trans 'Is PIK' %}</th>
|
<th scope="row">{% trans 'Is PIK' %}</th>
|
||||||
<td class="align-middle">
|
<td class="align-middle">
|
||||||
|
@ -71,7 +71,6 @@ class CompensationWorkflowTestCase(BaseWorkflowTestCase):
|
|||||||
self.assertEqual(new_compensation.title, test_title)
|
self.assertEqual(new_compensation.title, test_title)
|
||||||
self.assert_equal_geometries(new_compensation.geometry.geom, test_geom)
|
self.assert_equal_geometries(new_compensation.geometry.geom, test_geom)
|
||||||
self.assertEqual(new_compensation.log.count(), 1)
|
self.assertEqual(new_compensation.log.count(), 1)
|
||||||
self.assertEqual(new_compensation.created, new_compensation.modified)
|
|
||||||
|
|
||||||
# Expect logs to be set
|
# Expect logs to be set
|
||||||
self.assertEqual(pre_creation_intervention_log_count + 1, self.intervention.log.count())
|
self.assertEqual(pre_creation_intervention_log_count + 1, self.intervention.log.count())
|
||||||
|
@ -65,7 +65,6 @@ class EcoAccountWorkflowTestCase(BaseWorkflowTestCase):
|
|||||||
self.assertEqual(acc.deductable_rest, test_deductable_surface)
|
self.assertEqual(acc.deductable_rest, test_deductable_surface)
|
||||||
self.assert_equal_geometries(acc.geometry.geom, test_geom)
|
self.assert_equal_geometries(acc.geometry.geom, test_geom)
|
||||||
self.assertEqual(acc.log.count(), 1)
|
self.assertEqual(acc.log.count(), 1)
|
||||||
self.assertEqual(acc.created, acc.modified)
|
|
||||||
|
|
||||||
# Expect logs to be set
|
# Expect logs to be set
|
||||||
self.assertEqual(acc.log.count(), 1)
|
self.assertEqual(acc.log.count(), 1)
|
||||||
|
@ -46,8 +46,6 @@ def index_view(request: HttpRequest):
|
|||||||
compensations = Compensation.objects.filter(
|
compensations = Compensation.objects.filter(
|
||||||
deleted=None, # only show those which are not deleted individually
|
deleted=None, # only show those which are not deleted individually
|
||||||
intervention__deleted=None, # and don't show the ones whose intervention has been deleted
|
intervention__deleted=None, # and don't show the ones whose intervention has been deleted
|
||||||
).order_by(
|
|
||||||
"-modified__timestamp"
|
|
||||||
)
|
)
|
||||||
table = CompensationTable(
|
table = CompensationTable(
|
||||||
request=request,
|
request=request,
|
||||||
|
@ -41,8 +41,6 @@ def index_view(request: HttpRequest):
|
|||||||
template = "generic_index.html"
|
template = "generic_index.html"
|
||||||
eco_accounts = EcoAccount.objects.filter(
|
eco_accounts = EcoAccount.objects.filter(
|
||||||
deleted=None,
|
deleted=None,
|
||||||
).order_by(
|
|
||||||
"-modified__timestamp"
|
|
||||||
)
|
)
|
||||||
table = EcoAccountTable(
|
table = EcoAccountTable(
|
||||||
request=request,
|
request=request,
|
||||||
|
@ -81,7 +81,6 @@ class NewEmaForm(AbstractCompensationForm, CompensationResponsibleFormMixin, Pik
|
|||||||
title=title,
|
title=title,
|
||||||
responsible=responsible,
|
responsible=responsible,
|
||||||
created=action,
|
created=action,
|
||||||
modified=action,
|
|
||||||
comment=comment,
|
comment=comment,
|
||||||
is_pik=is_pik,
|
is_pik=is_pik,
|
||||||
)
|
)
|
||||||
|
@ -16,6 +16,10 @@
|
|||||||
<th scope="row">{% trans 'Conservation office' %}</th>
|
<th scope="row">{% trans 'Conservation office' %}</th>
|
||||||
<td class="align-middle">{{obj.responsible.conservation_office.str_as_office|default_if_none:""}}</td>
|
<td class="align-middle">{{obj.responsible.conservation_office.str_as_office|default_if_none:""}}</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th scope="row">{% trans 'Conservation office file number' %}</th>
|
||||||
|
<td class="align-middle">{{obj.responsible.conservation_file_number|default_if_none:""}}</td>
|
||||||
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row">{% trans 'Is PIK' %}</th>
|
<th scope="row">{% trans 'Is PIK' %}</th>
|
||||||
<td class="align-middle">
|
<td class="align-middle">
|
||||||
|
@ -62,7 +62,6 @@ class EmaWorkflowTestCase(BaseWorkflowTestCase):
|
|||||||
self.assertEqual(ema.title, test_title)
|
self.assertEqual(ema.title, test_title)
|
||||||
self.assert_equal_geometries(ema.geometry.geom, test_geom)
|
self.assert_equal_geometries(ema.geometry.geom, test_geom)
|
||||||
self.assertEqual(ema.log.count(), 1)
|
self.assertEqual(ema.log.count(), 1)
|
||||||
self.assertEqual(ema.created, ema.modified)
|
|
||||||
|
|
||||||
# Expect logs to be set
|
# Expect logs to be set
|
||||||
self.assertEqual(ema.log.count(), 1)
|
self.assertEqual(ema.log.count(), 1)
|
||||||
|
@ -39,8 +39,6 @@ def index_view(request: HttpRequest):
|
|||||||
template = "generic_index.html"
|
template = "generic_index.html"
|
||||||
emas = Ema.objects.filter(
|
emas = Ema.objects.filter(
|
||||||
deleted=None,
|
deleted=None,
|
||||||
).order_by(
|
|
||||||
"-modified__timestamp"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
table = EmaTable(
|
table = EmaTable(
|
||||||
|
@ -270,7 +270,6 @@ class NewInterventionForm(BaseForm):
|
|||||||
responsible=responsibility_data,
|
responsible=responsibility_data,
|
||||||
legal=legal_data,
|
legal=legal_data,
|
||||||
created=action,
|
created=action,
|
||||||
modified=action,
|
|
||||||
comment=comment,
|
comment=comment,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -29,10 +29,18 @@
|
|||||||
<th scope="row">{% trans 'Registration office' %}</th>
|
<th scope="row">{% trans 'Registration office' %}</th>
|
||||||
<td class="align-middle">{{obj.responsible.registration_office.str_as_office|default_if_none:""}}</td>
|
<td class="align-middle">{{obj.responsible.registration_office.str_as_office|default_if_none:""}}</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th scope="row">{% trans 'Registration office file number' %}</th>
|
||||||
|
<td class="align-middle">{{obj.responsible.registration_file_number|default_if_none:""}}</td>
|
||||||
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row">{% trans 'Conservation office' %}</th>
|
<th scope="row">{% trans 'Conservation office' %}</th>
|
||||||
<td class="align-middle">{{obj.responsible.conservation_office.str_as_office|default_if_none:""}}</td>
|
<td class="align-middle">{{obj.responsible.conservation_office.str_as_office|default_if_none:""}}</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th scope="row">{% trans 'Conservation office file number' %}</th>
|
||||||
|
<td class="align-middle">{{obj.responsible.conservation_file_number|default_if_none:""}}</td>
|
||||||
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row">{% trans 'Compensations' %}</th>
|
<th scope="row">{% trans 'Compensations' %}</th>
|
||||||
<td class="align-middle">
|
<td class="align-middle">
|
||||||
|
@ -78,7 +78,6 @@ class InterventionWorkflowTestCase(BaseWorkflowTestCase):
|
|||||||
self.assertEqual(1, obj.log.count())
|
self.assertEqual(1, obj.log.count())
|
||||||
self.assertEqual(obj.log.first().action, UserAction.CREATED)
|
self.assertEqual(obj.log.first().action, UserAction.CREATED)
|
||||||
self.assertEqual(obj.log.first().user, self.superuser)
|
self.assertEqual(obj.log.first().user, self.superuser)
|
||||||
self.assertEqual(obj.created, obj.modified)
|
|
||||||
except ObjectDoesNotExist:
|
except ObjectDoesNotExist:
|
||||||
# Fail if there is no such object
|
# Fail if there is no such object
|
||||||
self.fail()
|
self.fail()
|
||||||
|
@ -45,8 +45,6 @@ def index_view(request: HttpRequest):
|
|||||||
deleted=None, # not deleted
|
deleted=None, # not deleted
|
||||||
).select_related(
|
).select_related(
|
||||||
"legal"
|
"legal"
|
||||||
).order_by(
|
|
||||||
"-modified__timestamp"
|
|
||||||
)
|
)
|
||||||
table = InterventionTable(
|
table = InterventionTable(
|
||||||
request=request,
|
request=request,
|
||||||
|
@ -1,137 +0,0 @@
|
|||||||
"""
|
|
||||||
Author: Michel Peltriaux
|
|
||||||
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
|
||||||
Contact: michel.peltriaux@sgdnord.rlp.de
|
|
||||||
Created on: 26.10.22
|
|
||||||
|
|
||||||
"""
|
|
||||||
import zipfile
|
|
||||||
from io import BytesIO
|
|
||||||
|
|
||||||
from django.core.mail import EmailMessage
|
|
||||||
from django.utils import timezone
|
|
||||||
from django.utils.datetime_safe import datetime
|
|
||||||
|
|
||||||
from analysis.utils.excel.excel import TempExcelFile
|
|
||||||
from analysis.utils.report import TimespanReport
|
|
||||||
from codelist.models import KonovaCode
|
|
||||||
from codelist.settings import CODELIST_CONSERVATION_OFFICE_ID
|
|
||||||
from konova.management.commands.setup import BaseKonovaCommand
|
|
||||||
from konova.sub_settings.django_settings import DEFAULT_FROM_EMAIL, ADMINS
|
|
||||||
|
|
||||||
|
|
||||||
class Command(BaseKonovaCommand):
|
|
||||||
help = "Generates reports for conservation offices"
|
|
||||||
|
|
||||||
__from_key = "from"
|
|
||||||
__to_key = "to"
|
|
||||||
__for_key = "for"
|
|
||||||
|
|
||||||
from_date = None
|
|
||||||
to_date = None
|
|
||||||
offices = None
|
|
||||||
|
|
||||||
def add_arguments(self, parser):
|
|
||||||
try:
|
|
||||||
parser.add_argument(f"--{self.__from_key}", type=str, nargs="+")
|
|
||||||
parser.add_argument(f"--{self.__to_key}", type=str, nargs="+")
|
|
||||||
parser.add_argument(f"--{self.__for_key}", type=str, nargs="*")
|
|
||||||
except ValueError as e:
|
|
||||||
self._write_error(f"Argument error: {e}")
|
|
||||||
exit(-1)
|
|
||||||
|
|
||||||
def handle(self, *args, **options):
|
|
||||||
self.__check_arguments(options)
|
|
||||||
in_memory_zipped_reports = self.generate_reports_in_memory_zipped()
|
|
||||||
self.send_mail_to_admins(in_memory_zipped_reports)
|
|
||||||
|
|
||||||
def generate_reports_in_memory_zipped(self):
|
|
||||||
"""
|
|
||||||
Generates in-memory reports and zips into in-memory zip file.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
"""
|
|
||||||
memory_zip = BytesIO()
|
|
||||||
with zipfile.ZipFile(memory_zip, "w", compression=zipfile.ZIP_DEFLATED) as zf:
|
|
||||||
for office in self.offices:
|
|
||||||
self._write_warning(f" Process report for {office.long_name}...")
|
|
||||||
report = TimespanReport(office.id, self.from_date, self.to_date)
|
|
||||||
excel_file = TempExcelFile(report.excel_template_path, report.excel_map)
|
|
||||||
zf.writestr(zinfo_or_arcname=f"{office.long_name}_{self.from_date.date()}_{self.to_date.date()}.xlsx", data=excel_file.stream)
|
|
||||||
self._write_success(f"Reports generated for {self.offices.count()} offices and zipped.")
|
|
||||||
return memory_zip.getvalue()
|
|
||||||
|
|
||||||
def __check_arguments(self, options):
|
|
||||||
"""
|
|
||||||
Checks given parameters for validity
|
|
||||||
|
|
||||||
Args:
|
|
||||||
options ():
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
"""
|
|
||||||
_from_value = options.get(self.__from_key, [None])[0]
|
|
||||||
_to_value = options.get(self.__to_key, [None])[0]
|
|
||||||
_for_value = options.get(self.__for_key, [])
|
|
||||||
|
|
||||||
# Check ISO dates
|
|
||||||
try:
|
|
||||||
_from_date = timezone.make_aware(datetime.fromisoformat(_from_value))
|
|
||||||
_to_date = timezone.make_aware(datetime.fromisoformat(_to_value))
|
|
||||||
except Exception as e:
|
|
||||||
self._write_warning(f"One of the dates is not in ISO format (YYYY-MM-DD). {e}")
|
|
||||||
exit(-1)
|
|
||||||
|
|
||||||
# Check conservation office IDs
|
|
||||||
_filter = {
|
|
||||||
"is_archived": False,
|
|
||||||
"is_leaf": True,
|
|
||||||
"code_lists__in": [CODELIST_CONSERVATION_OFFICE_ID],
|
|
||||||
}
|
|
||||||
offices = KonovaCode.objects.filter(**_filter)
|
|
||||||
if _for_value is not None and len(_for_value) != 0:
|
|
||||||
# Specifc offices requested
|
|
||||||
offices = offices.filter(short_name__in=_for_value)
|
|
||||||
all_requested_offices_exist = offices.count() == len(_for_value)
|
|
||||||
if not all_requested_offices_exist:
|
|
||||||
offices_short_name = set(offices.values_list("short_name", flat=True))
|
|
||||||
missing_ids = []
|
|
||||||
for val in _for_value:
|
|
||||||
if val not in offices_short_name:
|
|
||||||
missing_ids.append(val)
|
|
||||||
self._write_warning(
|
|
||||||
f"Unknown offices: {missing_ids}"
|
|
||||||
)
|
|
||||||
exit(-1)
|
|
||||||
|
|
||||||
self.offices = offices
|
|
||||||
self.from_date = _from_date
|
|
||||||
self.to_date = _to_date
|
|
||||||
|
|
||||||
def send_mail_to_admins(self, attachment):
|
|
||||||
office_names = [office.long_name for office in self.offices]
|
|
||||||
admin_mails = [admin[1] for admin in ADMINS]
|
|
||||||
|
|
||||||
date_from_date = self.from_date.date()
|
|
||||||
date_to_date = self.to_date.date()
|
|
||||||
|
|
||||||
mail = EmailMessage(
|
|
||||||
subject="Konova reports generated",
|
|
||||||
body=f"Zipped reports attached from {date_from_date} to {date_to_date} for {office_names}.",
|
|
||||||
from_email=DEFAULT_FROM_EMAIL,
|
|
||||||
to=admin_mails,
|
|
||||||
attachments=[
|
|
||||||
(
|
|
||||||
f"reports_{date_from_date}_{date_to_date}.zip",
|
|
||||||
attachment,
|
|
||||||
"application/zip"
|
|
||||||
)
|
|
||||||
]
|
|
||||||
)
|
|
||||||
success = mail.send()
|
|
||||||
if success == 1:
|
|
||||||
self._write_success(f"Mails with zip as attachment sent to {admin_mails}.")
|
|
||||||
else:
|
|
||||||
self._write_error(f"Something went wrong during mail sending. Returned sending code was '{success}'")
|
|
Loading…
Reference in New Issue
Block a user