Skip to content

Commit

Permalink
Add property managers report
Browse files Browse the repository at this point in the history
  • Loading branch information
indigane committed Feb 10, 2025
1 parent 2f233fd commit f3e903a
Show file tree
Hide file tree
Showing 6 changed files with 162 additions and 0 deletions.
60 changes: 60 additions & 0 deletions backend/hitas/services/reports.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,15 @@
from hitas.models import ApartmentSale, Owner
from hitas.models.housing_company import (
HitasType,
HousingCompany,
HousingCompanyWithRegulatedReportAnnotations,
HousingCompanyWithStateReportAnnotations,
HousingCompanyWithUnregulatedReportAnnotations,
RegulationStatus,
)
from hitas.models.indices import SurfaceAreaPriceCeiling
from hitas.models.ownership import Ownership, OwnershipWithApartmentCount
from hitas.models.property_manager import PropertyManager
from hitas.utils import format_sheet, resize_columns

T = TypeVar("T")
Expand Down Expand Up @@ -71,6 +73,12 @@ class UnregulatedHousingCompaniesReportColumns(NamedTuple):
apartment_count: int | str


class PropertyManagerReportColumns(NamedTuple):
property_manager_name: str
property_manager_email: str
housing_company_name: str


class HousingCompanyStatesCount(TypedDict):
housing_company_count: int
apartment_count: int
Expand Down Expand Up @@ -705,6 +713,58 @@ def build_unregulated_housing_companies_report_excel(
return workbook


def build_property_managers_report_excel(
housing_companies: list[HousingCompany],
property_managers_with_no_housing_company: list[PropertyManager],
) -> Workbook:
workbook = Workbook()
worksheet: Worksheet = workbook.active

column_headers = PropertyManagerReportColumns(
property_manager_name="Isännöitsijätoimisto/isännöitsijä",
property_manager_email="Sähköpostiosoite",
housing_company_name="Taloyhtiön nimi",
)
worksheet.append(column_headers)

for housing_company in housing_companies:
worksheet.append(
PropertyManagerReportColumns(
property_manager_name=housing_company.property_manager.name,
property_manager_email=housing_company.property_manager.email,
housing_company_name=housing_company.display_name,
)
)

for property_manager in property_managers_with_no_housing_company:
worksheet.append(
PropertyManagerReportColumns(
property_manager_name=property_manager.name,
property_manager_email=property_manager.email,
housing_company_name="",
)
)

last_row = worksheet.max_row
worksheet.auto_filter.ref = worksheet.dimensions

column_letters = string.ascii_uppercase[: len(column_headers)]

format_sheet(
worksheet,
formatting_rules={
# Add a border to the header row
**{f"{letter}1": {"border": Border(bottom=Side(style="thin"))} for letter in column_letters},
# Add a border to the last data row
**{f"{letter}{last_row}": {"border": Border(bottom=Side(style="thin"))} for letter in column_letters},
},
)

resize_columns(worksheet)
worksheet.protection.sheet = True
return workbook


def build_housing_company_state_report_excel(
housing_companies: list[HousingCompanyWithStateReportAnnotations],
) -> Workbook:
Expand Down
50 changes: 50 additions & 0 deletions backend/hitas/tests/apis/test_api_reports.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
ThirtyYearRegulationResultsRow,
)
from hitas.models.housing_company import HitasType, RegulationStatus
from hitas.models.property_manager import PropertyManager
from hitas.models.thirty_year_regulation import FullSalesData, RegulationResult
from hitas.tests.apis.helpers import HitasAPIClient
from hitas.tests.factories import (
Expand All @@ -31,6 +32,7 @@
)
from hitas.tests.factories.apartment import ApartmentMaximumPriceCalculationFactory
from hitas.tests.factories.indices import SurfaceAreaPriceCeilingFactory
from hitas.tests.factories.property_manager import PropertyManagerFactory

# Sales report

Expand Down Expand Up @@ -1011,6 +1013,54 @@ def test__api__unregulated_housing_companies_report__multiple_housing_companies(
]


# Property managers and their housing companies report


@pytest.mark.django_db
def test__api__property_managers_report(api_client: HitasAPIClient):
property_manager_1: PropertyManager = PropertyManagerFactory.create(name="Property Manager A")
housing_company_1: HousingCompany = HousingCompanyFactory.create(property_manager=property_manager_1)

property_manager_2: PropertyManager = PropertyManagerFactory.create(name="Property Manager B")
housing_company_2: HousingCompany = HousingCompanyFactory.create(property_manager=property_manager_2)
housing_company_3: HousingCompany = HousingCompanyFactory.create(property_manager=property_manager_2)

property_manager_3: PropertyManager = PropertyManagerFactory.create(name="Property Manager C")
housing_company_4: HousingCompany = HousingCompanyFactory.create(property_manager=property_manager_3)

property_manager_4: PropertyManager = PropertyManagerFactory.create(name="Property Manager A2")

url = reverse("hitas:property-managers-report-list")
response: HttpResponse = api_client.get(url)

workbook: Workbook = load_workbook(BytesIO(response.content), data_only=False)
worksheet: Worksheet = workbook.worksheets[0]

rows = list(worksheet.values)
assert len(rows[0][0]) > 0, "Row 1 column 1 should have a title"
assert len(rows[0][1]) > 0, "Row 1 column 2 should have a title"
assert len(rows[0][2]) > 0, "Row 1 column 3 should have a title"
# Property manager 1
assert rows[1][0] == property_manager_1.name
assert rows[1][1] == property_manager_1.email
assert rows[1][2] == housing_company_1.display_name
# Property manager 2 - Two housing companies
assert rows[2][0] == property_manager_2.name
assert rows[2][1] == property_manager_2.email
assert rows[2][2] == housing_company_2.display_name
assert rows[3][0] == property_manager_2.name
assert rows[3][1] == property_manager_2.email
assert rows[3][2] == housing_company_3.display_name
# Property manager 3
assert rows[4][0] == property_manager_3.name
assert rows[4][1] == property_manager_3.email
assert rows[4][2] == housing_company_4.display_name
# Property manager 4 - No housing company
assert rows[5][0] == property_manager_4.name
assert rows[5][1] == property_manager_4.email
assert rows[5][2] is None


# Housing company state report


Expand Down
7 changes: 7 additions & 0 deletions backend/hitas/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,13 @@
basename="unregulated-housing-companies-report",
)

# /api/v1/reports/download-property-managers-report
router.register(
r"reports/download-property-managers-report",
views.PropertyManagersReportView,
basename="property-managers-report",
)

# /api/v1/reports/housing-company-states
router.register(
r"reports/housing-company-states",
Expand Down
1 change: 1 addition & 0 deletions backend/hitas/views/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
MultipleOwnershipsReportView,
OwnershipsByCompanyJSONReportView,
OwnershipsByHousingCompanyReport,
PropertyManagersReportView,
RegulatedHousingCompaniesReportView,
RegulatedOwnershipsReportView,
SalesAndMaximumPricesReportView,
Expand Down
23 changes: 23 additions & 0 deletions backend/hitas/views/reports.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
from rest_framework.viewsets import ViewSet

from hitas.models import HousingCompany, Owner, Ownership
from hitas.models.housing_company import RegulationStatus
from hitas.models.property_manager import PropertyManager
from hitas.services.apartment_sale import find_sales_on_interval_for_reporting
from hitas.services.housing_company import (
find_half_hitas_housing_companies_for_reporting,
Expand All @@ -24,6 +26,7 @@
build_housing_company_state_report_excel,
build_multiple_ownerships_report_excel,
build_owners_by_housing_companies_report_excel,
build_property_managers_report_excel,
build_regulated_housing_companies_report_excel,
build_regulated_ownerships_report_excel,
build_sales_and_maximum_prices_report_excel,
Expand Down Expand Up @@ -111,6 +114,26 @@ def list(self, request: Request, *args, **kwargs) -> HttpResponse:
return get_excel_response(filename=filename, excel=workbook)


class PropertyManagersReportView(ViewSet):
renderer_classes = [HitasJSONRenderer, ExcelRenderer]

def list(self, request: Request, *args, **kwargs) -> HttpResponse:
housing_companies = list(
HousingCompany.objects.filter(
property_manager__isnull=False,
regulation_status=RegulationStatus.REGULATED,
)
.select_related("property_manager")
.order_by("property_manager__name")
)
property_managers_with_no_housing_company = list(
PropertyManager.objects.filter(housing_companies__isnull=True).order_by("name")
)
workbook = build_property_managers_report_excel(housing_companies, property_managers_with_no_housing_company)
filename = "Isännöitsijät.xlsx"
return get_excel_response(filename=filename, excel=workbook)


class HousingCompanyStatesReportView(ViewSet):
renderer_classes = [HitasJSONRenderer, ExcelRenderer]

Expand Down
21 changes: 21 additions & 0 deletions backend/openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4631,6 +4631,27 @@ paths:
"500":
$ref: "#/components/responses/InternalServerError"

/api/v1/reports/download-property-managers-report:
get:
description: Download an Excel report of property managers and their housing companies
operationId: fetch-property-managers-report-excel
tags:
- Reports
responses:
"200":
description: Successfully downloaded a report of property managers
content:
application/vnd.openxmlformats-officedocument.spreadsheetml.sheet:
schema:
type: string
format: binary
"400":
$ref: "#/components/responses/BadRequest"
"404":
$ref: "#/components/responses/NotFound"
"500":
$ref: "#/components/responses/InternalServerError"

/api/v1/reports/housing-company-states:
get:
description: Get housing companies counted by their states
Expand Down

0 comments on commit f3e903a

Please sign in to comment.