Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Tasotarkistus data to rent model #773

Merged
merged 9 commits into from
Dec 5, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from django.utils.dateparse import parse_datetime

from leasing.models.rent import (
IndexNumberYearly,
IndexPointFigureYearly,
OldDwellingsInHousingCompaniesPriceIndex,
)

Expand Down Expand Up @@ -316,15 +316,15 @@ def _update_or_create_index_numbers(

for dp in data_points:
year = int(dp["key"][year_key_pos])
number = _cast_index_number_to_float_or_none(dp["values"][number_value_pos])
value = _cast_index_number_to_float_or_none(dp["values"][number_value_pos])
region = dp["key"][region_key_pos]
comment = _find_comment_for_value(dp, comments, columns)
# TODO verify in 5.9. meeting: should we exclude "ennakkotieto" commented values?
_, created = IndexNumberYearly.objects.update_or_create(
_, created = IndexPointFigureYearly.objects.update_or_create(
index=index,
year=year,
defaults={
"number": number,
"value": value,
"region": region,
"comment": comment,
},
Expand Down
9 changes: 9 additions & 0 deletions leasing/management/commands/set_group_field_permissions.py
Original file line number Diff line number Diff line change
Expand Up @@ -499,6 +499,15 @@
6: "view",
7: "change",
},
"old_dwellings_in_housing_companies_price_index": {
1: "view",
2: "view",
3: "view",
4: "view",
5: "view",
6: "view",
7: "change",
},
},
"tenant": {"reference": {6: "change"}, "tenantcontact_set": {6: "change"}},
"leasearea": {
Expand Down
9 changes: 9 additions & 0 deletions leasing/management/commands/set_group_model_permissions.py
Original file line number Diff line number Diff line change
Expand Up @@ -973,6 +973,15 @@
6: ("view",),
7: ("view", "add", "change", "delete"),
},
"olddwellingsinhousingcompaniespriceindex": {
1: ("view",),
2: ("view",),
3: ("view",),
4: ("view",),
5: ("view",),
6: ("view",),
7: ("view", "add", "change", "delete"),
},
"uidata": {
1: ("view", "add", "change", "delete"),
2: ("view", "add", "change", "delete"),
Expand Down
95 changes: 95 additions & 0 deletions leasing/migrations/0083_indexpointfigureyearly_and_more.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
# Generated by Django 4.2.16 on 2024-11-22 11:42

from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

dependencies = [
("leasing", "0082_remove_rent_seasonal_end_day_and_more"),
]

operations = [
migrations.CreateModel(
name="IndexPointFigureYearly",
fields=[
(
"id",
models.AutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"created_at",
models.DateTimeField(
auto_now_add=True, verbose_name="Time created"
),
),
(
"modified_at",
models.DateTimeField(auto_now=True, verbose_name="Time modified"),
),
(
"value",
models.DecimalField(
decimal_places=1, max_digits=8, null=True, verbose_name="Value"
),
),
("year", models.PositiveSmallIntegerField(verbose_name="Year")),
(
"region",
models.CharField(blank=True, max_length=255, verbose_name="Region"),
),
("comment", models.TextField(blank=True, verbose_name="Comment")),
(
"index",
models.ForeignKey(
on_delete=django.db.models.deletion.PROTECT,
related_name="point_figures",
to="leasing.olddwellingsinhousingcompaniespriceindex",
verbose_name="Index",
),
),
],
options={
"verbose_name": "index number",
"verbose_name_plural": "index numbers",
"ordering": ("-index", "-year"),
},
),
migrations.AddField(
model_name="rent",
name="old_dwellings_in_housing_companies_price_index",
field=models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.PROTECT,
related_name="+",
to="leasing.olddwellingsinhousingcompaniespriceindex",
verbose_name="Old dwellings in housing companies price index",
),
),
migrations.AddField(
model_name="rent",
name="start_price_index_point_figure",
field=models.DecimalField(
decimal_places=1,
max_digits=8,
null=True,
verbose_name="Start price index point figure",
),
),
migrations.DeleteModel(
name="IndexNumberYearly",
),
migrations.AddConstraint(
model_name="indexpointfigureyearly",
constraint=models.UniqueConstraint(
fields=("index", "year"), name="unique_price_index_number"
),
),
]
4 changes: 2 additions & 2 deletions leasing/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@
FixedInitialYearRent,
Index,
IndexAdjustedRent,
IndexNumberYearly,
IndexPointFigureYearly,
LeaseBasisOfRent,
LeaseBasisOfRentManagementSubvention,
LeaseBasisOfRentTemporarySubvention,
Expand Down Expand Up @@ -170,7 +170,7 @@
"Hitas",
"Index",
"IndexAdjustedRent",
"IndexNumberYearly",
"IndexPointFigureYearly",
"InfillDevelopmentCompensation",
"InfillDevelopmentCompensationAttachment",
"InfillDevelopmentCompensationDecision",
Expand Down
152 changes: 89 additions & 63 deletions leasing/models/rent.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,65 @@ class Meta(NameModel.Meta):
verbose_name_plural = pgettext_lazy("Model name", "Rent intended uses")


class OldDwellingsInHousingCompaniesPriceIndex(TimeStampedModel):
juho-kettunen-nc marked this conversation as resolved.
Show resolved Hide resolved
"""
In Finnish: Vanhojen osakeasuntojen hintaindeksi

From my understanding, a unique price index is identified by the API
database table URL and code of the index. A single table can hold multiple
indexes, and a single code can be used in multiple tables.

Members:
code: Code for the index's table column. Example: "ketj_P_QA_T". \
Same code is shared between tables for different intervals, e.g. \
yearly or quarterly.
name: Name of the index. Example: "Index (2020=100)".
comment: Comment for the index's table column.
source: Source of the data.
source_table_updated: UTC timestamp when the source table was last updated.
source_table_label: Label for the source table.
url: API endpoint URL.
"""

# Maximum lengths are arbitrary, but set to avoid extra large input.
CHARFIELD_MAX_LENGTH = 255

code = models.CharField(
verbose_name=_("Index code"),
max_length=CHARFIELD_MAX_LENGTH,
unique=True,
)
name = models.CharField(
verbose_name=_("Index name"),
max_length=CHARFIELD_MAX_LENGTH,
)
comment = models.TextField(verbose_name=_("Region"), blank=True)
source = models.CharField(
verbose_name=_("Data source"),
blank=True,
max_length=CHARFIELD_MAX_LENGTH,
)
source_table_updated = models.DateTimeField(
verbose_name=_("Source table updated"), null=True
)
source_table_label = models.TextField(
verbose_name=_("Source table label"),
blank=True,
)
url = models.CharField(
verbose_name=_("API endpoint URL"),
max_length=CHARFIELD_MAX_LENGTH,
)

class Meta:
verbose_name = pgettext_lazy(
"model name", "price index of old dwellings in housing companies"
)
verbose_name_plural = pgettext_lazy(
"model name", "price indexes of old dwellings in housing companies"
)


class Rent(TimeStampedSafeDeleteModel):
"""
In Finnish: Vuokran perustiedot
Expand Down Expand Up @@ -241,6 +300,24 @@ class Rent(TimeStampedSafeDeleteModel):
null=True,
)

# In Finnish: Tasotarkistusindeksi
old_dwellings_in_housing_companies_price_index = models.ForeignKey(
OldDwellingsInHousingCompaniesPriceIndex,
verbose_name=_("Old dwellings in housing companies price index"),
related_name="+",
on_delete=models.PROTECT,
blank=True,
null=True,
)

# In Finnish: Tasotarkistusindeksin pisteluku vuokran alkaessa (edellisen vuoden keskiarvo)
start_price_index_point_figure = models.DecimalField(
verbose_name=_("Start price index point figure"),
decimal_places=1,
max_digits=8,
null=True,
)

Copy link
Contributor

@juho-kettunen-nc juho-kettunen-nc Nov 22, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Then I think we need a 3rd new property: type of the tasotarkistus (20v/20v) or (20v/10v).

The Rent class is getting slightly populated, but it might still be OK because I just removed 4 fields about the seasonal rent.

Another possibility is to create a new model for PeriodicRentAdjustment which holds these 3 fields (index, adjustment type, and starting point figure). Then Rent would have a single ForeignKey property like periodic_rent_adjustment that can be null if the rent doesn't use the feature.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you @henrinie-nc think?

recursive_get_related_skip_relations = ["lease"]

class Meta:
Expand Down Expand Up @@ -753,6 +830,14 @@ def is_active_on_period(self, date_range_start, date_range_end):

return False

def set_start_price_index_point_figure(self):
if self.old_dwellings_in_housing_companies_price_index:
start_index_number_yearly = IndexPointFigureYearly.objects.get(
index=self.old_dwellings_in_housing_companies_price_index,
year=self.lease.start_date.year - 1,
)
self.start_price_index_point_figure = start_index_number_yearly.value


class RentDueDate(TimeStampedSafeDeleteModel):
"""
Expand Down Expand Up @@ -1408,66 +1493,7 @@ class LegacyIndex(models.Model):
)


class OldDwellingsInHousingCompaniesPriceIndex(TimeStampedModel):
"""
In Finnish: Vanhojen osakeasuntojen hintaindeksi

From my understanding, a unique price index is identified by the API
database table URL and code of the index. A single table can hold multiple
indexes, and a single code can be used in multiple tables.

Members:
code: Code for the index's table column. Example: "ketj_P_QA_T". \
Same code is shared between tables for different intervals, e.g. \
yearly or quarterly.
name: Name of the index. Example: "Index (2020=100)".
comment: Comment for the index's table column.
source: Source of the data.
source_table_updated: UTC timestamp when the source table was last updated.
source_table_label: Label for the source table.
url: API endpoint URL.
"""

# Maximum lengths are arbitrary, but set to avoid extra large input.
CHARFIELD_MAX_LENGTH = 255

code = models.CharField(
verbose_name=_("Index code"),
max_length=CHARFIELD_MAX_LENGTH,
unique=True,
)
name = models.CharField(
verbose_name=_("Index name"),
max_length=CHARFIELD_MAX_LENGTH,
)
comment = models.TextField(verbose_name=_("Region"), blank=True)
source = models.CharField(
verbose_name=_("Data source"),
blank=True,
max_length=CHARFIELD_MAX_LENGTH,
)
source_table_updated = models.DateTimeField(
verbose_name=_("Source table updated"), null=True
)
source_table_label = models.TextField(
verbose_name=_("Source table label"),
blank=True,
)
url = models.CharField(
verbose_name=_("API endpoint URL"),
max_length=CHARFIELD_MAX_LENGTH,
)

class Meta:
verbose_name = pgettext_lazy(
"model name", "price index of old dwellings in housing companies"
)
verbose_name_plural = pgettext_lazy(
"model name", "price indexes of old dwellings in housing companies"
)


class IndexNumberYearly(TimeStampedModel):
class IndexPointFigureYearly(TimeStampedModel):
"""
In Finnish: Indeksipisteluku, vuosittain

Expand All @@ -1493,14 +1519,14 @@ class IndexNumberYearly(TimeStampedModel):
OldDwellingsInHousingCompaniesPriceIndex,
verbose_name=_("Index"),
on_delete=models.PROTECT,
related_name="numbers",
related_name="point_figures",
)
# max_digits is arbitrary for the number. No need to limit it, although 7
# should be enough if the numbers are at most in the 100s of thousands.
# Largest index number in the system at the moment is year 1914's index
# with a number around 260 000.
number = models.DecimalField(
verbose_name=_("Index number"),
value = models.DecimalField(
verbose_name=_("Value"),
decimal_places=1,
max_digits=8,
null=True,
Expand Down
Loading