pyfunda 2.4.0__tar.gz → 2.6.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pyfunda-2.6.0/.dockerignore +6 -0
- pyfunda-2.6.0/Dockerfile +13 -0
- {pyfunda-2.4.0 → pyfunda-2.6.0}/PKG-INFO +1 -1
- {pyfunda-2.4.0 → pyfunda-2.6.0}/funda/funda.py +60 -0
- {pyfunda-2.4.0 → pyfunda-2.6.0}/pyproject.toml +1 -1
- {pyfunda-2.4.0 → pyfunda-2.6.0}/.github/FUNDING.yml +0 -0
- {pyfunda-2.4.0 → pyfunda-2.6.0}/.github/workflows/publish.yml +0 -0
- {pyfunda-2.4.0 → pyfunda-2.6.0}/.gitignore +0 -0
- {pyfunda-2.4.0 → pyfunda-2.6.0}/LICENSE +0 -0
- {pyfunda-2.4.0 → pyfunda-2.6.0}/README.md +0 -0
- {pyfunda-2.4.0 → pyfunda-2.6.0}/examples/analysis.ipynb +0 -0
- {pyfunda-2.4.0 → pyfunda-2.6.0}/examples/export_to_csv.py +0 -0
- {pyfunda-2.4.0 → pyfunda-2.6.0}/examples/new_listings_alert.py +0 -0
- {pyfunda-2.4.0 → pyfunda-2.6.0}/examples/poll_new_listings.py +0 -0
- {pyfunda-2.4.0 → pyfunda-2.6.0}/examples/price_history.py +0 -0
- {pyfunda-2.4.0 → pyfunda-2.6.0}/examples/price_tracker.py +0 -0
- {pyfunda-2.4.0 → pyfunda-2.6.0}/examples/search_sold.py +0 -0
- {pyfunda-2.4.0 → pyfunda-2.6.0}/funda/__init__.py +0 -0
- {pyfunda-2.4.0 → pyfunda-2.6.0}/funda/listing.py +0 -0
- {pyfunda-2.4.0 → pyfunda-2.6.0}/funda/py.typed +0 -0
- {pyfunda-2.4.0 → pyfunda-2.6.0}/test_all_flows.py +0 -0
pyfunda-2.6.0/Dockerfile
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
FROM ghcr.io/astral-sh/uv:0.6.6-python3.12-bookworm-slim
|
|
2
|
+
|
|
3
|
+
WORKDIR /app
|
|
4
|
+
|
|
5
|
+
COPY pyproject.toml uv.lock ./
|
|
6
|
+
RUN uv sync --frozen --no-install-project --no-cache
|
|
7
|
+
|
|
8
|
+
COPY . .
|
|
9
|
+
RUN uv sync --frozen --no-cache
|
|
10
|
+
|
|
11
|
+
ENV PATH="/app/.venv/bin:$PATH"
|
|
12
|
+
|
|
13
|
+
CMD ["bash"]
|
|
@@ -324,6 +324,9 @@ class Funda:
|
|
|
324
324
|
plot_max: int | None = None,
|
|
325
325
|
object_type: list[str] | None = None,
|
|
326
326
|
energy_label: list[str] | None = None,
|
|
327
|
+
construction_type: str | list[str] | None = None,
|
|
328
|
+
construction_year_min: int | None = None,
|
|
329
|
+
construction_year_max: int | None = None,
|
|
327
330
|
radius_km: int | None = None,
|
|
328
331
|
sort: str | None = None,
|
|
329
332
|
page: int = 0,
|
|
@@ -343,6 +346,9 @@ class Funda:
|
|
|
343
346
|
plot_max: Maximum plot area in m²
|
|
344
347
|
object_type: Property types (e.g. ["house", "apartment"])
|
|
345
348
|
energy_label: Energy labels (e.g. ["A", "A+", "A++"])
|
|
349
|
+
construction_type: "resale" or "newly_built", or a list of both
|
|
350
|
+
construction_year_min: Minimum construction year (maps to Funda periods)
|
|
351
|
+
construction_year_max: Maximum construction year (maps to Funda periods)
|
|
346
352
|
radius_km: Search radius in km (use with single location/postcode)
|
|
347
353
|
sort: Sort order - "newest", "oldest", "price_asc", "price_desc",
|
|
348
354
|
"area_asc", "area_desc", "plot_desc", "city", "postcode", or None
|
|
@@ -449,6 +455,41 @@ class Funda:
|
|
|
449
455
|
if energy_label:
|
|
450
456
|
params["energy_label"] = energy_label
|
|
451
457
|
|
|
458
|
+
# Construction type filter
|
|
459
|
+
if construction_type:
|
|
460
|
+
if isinstance(construction_type, str):
|
|
461
|
+
params["construction_type"] = [construction_type]
|
|
462
|
+
else:
|
|
463
|
+
params["construction_type"] = list(construction_type)
|
|
464
|
+
|
|
465
|
+
# Construction year filter (mapped to Funda's predefined periods)
|
|
466
|
+
if construction_year_min is not None or construction_year_max is not None:
|
|
467
|
+
period_boundaries = [1906, 1931, 1945, 1960, 1971, 1981, 1991, 2001, 2011, 2021]
|
|
468
|
+
all_periods = (
|
|
469
|
+
["before_1906"]
|
|
470
|
+
+ [f"from_{period_boundaries[i]}_to_{period_boundaries[i+1]-1}" for i in range(len(period_boundaries) - 1)]
|
|
471
|
+
+ ["after_2020"]
|
|
472
|
+
)
|
|
473
|
+
# Map each period to its year range for filtering
|
|
474
|
+
period_ranges = {
|
|
475
|
+
"before_1906": (0, 1905),
|
|
476
|
+
"from_1906_to_1930": (1906, 1930),
|
|
477
|
+
"from_1931_to_1944": (1931, 1944),
|
|
478
|
+
"from_1945_to_1959": (1945, 1959),
|
|
479
|
+
"from_1960_to_1970": (1960, 1970),
|
|
480
|
+
"from_1971_to_1980": (1971, 1980),
|
|
481
|
+
"from_1981_to_1990": (1981, 1990),
|
|
482
|
+
"from_1991_to_2000": (1991, 2000),
|
|
483
|
+
"from_2001_to_2010": (2001, 2010),
|
|
484
|
+
"from_2011_to_2020": (2011, 2020),
|
|
485
|
+
"after_2020": (2021, 9999),
|
|
486
|
+
}
|
|
487
|
+
year_min = construction_year_min or 0
|
|
488
|
+
year_max = construction_year_max or 9999
|
|
489
|
+
selected = [p for p in all_periods if period_ranges[p][1] >= year_min and period_ranges[p][0] <= year_max]
|
|
490
|
+
if selected:
|
|
491
|
+
params["construction_period"] = selected
|
|
492
|
+
|
|
452
493
|
# Build NDJSON query
|
|
453
494
|
index_line = json.dumps({"index": "listings-wonen-searcher-alias-prod"})
|
|
454
495
|
query_line = json.dumps({"id": "search_result_20250805", "params": params})
|
|
@@ -771,23 +812,42 @@ class Funda:
|
|
|
771
812
|
price = price_data.get("selling_price", [None])[0]
|
|
772
813
|
if not price:
|
|
773
814
|
price = price_data.get("rent_price", [None])[0] if price_data.get("rent_price") else None
|
|
815
|
+
price_condition = price_data.get("selling_price_condition") or price_data.get("rent_price_condition")
|
|
774
816
|
else:
|
|
775
817
|
price = price_data
|
|
818
|
+
price_condition = None
|
|
819
|
+
|
|
820
|
+
offering_types = source.get("offering_type", [])
|
|
821
|
+
offering_type = offering_types[0] if offering_types else None
|
|
822
|
+
|
|
823
|
+
agents = source.get("agent", [])
|
|
824
|
+
agent = agents[0] if agents else {}
|
|
776
825
|
|
|
777
826
|
listing_data = {
|
|
778
827
|
"global_id": int(hit.get("_id", 0)),
|
|
779
828
|
"title": f"{address.get('street_name', '')} {address.get('house_number', '')}".strip(),
|
|
829
|
+
"street_name": address.get("street_name"),
|
|
830
|
+
"house_number": address.get("house_number"),
|
|
831
|
+
"house_number_suffix": address.get("house_number_suffix"),
|
|
780
832
|
"city": address.get("city"),
|
|
781
833
|
"postcode": address.get("postal_code"),
|
|
782
834
|
"province": address.get("province"),
|
|
783
835
|
"neighbourhood": address.get("neighbourhood"),
|
|
784
836
|
"price": price,
|
|
837
|
+
"price_condition": price_condition,
|
|
785
838
|
"living_area": source.get("floor_area", [None])[0] if source.get("floor_area") else None,
|
|
786
839
|
"plot_area": source.get("plot_area_range", {}).get("gte"),
|
|
787
840
|
"bedrooms": source.get("number_of_bedrooms"),
|
|
841
|
+
"rooms": source.get("number_of_rooms"),
|
|
788
842
|
"energy_label": source.get("energy_label"),
|
|
789
843
|
"object_type": source.get("object_type"),
|
|
844
|
+
"offering_type": offering_type,
|
|
790
845
|
"construction_type": source.get("construction_type"),
|
|
846
|
+
"publish_date": source.get("publish_date"),
|
|
847
|
+
"detail_url": source.get("object_detail_page_relative_url"),
|
|
848
|
+
"broker_id": agent.get("id"),
|
|
849
|
+
"broker_name": agent.get("name"),
|
|
850
|
+
"broker_association": agent.get("association"),
|
|
791
851
|
"photos": source.get("thumbnail_id", [])[:5],
|
|
792
852
|
}
|
|
793
853
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|