Initial commit: Real Estate Registry app

This commit is contained in:
Georges Haddad
2026-04-10 15:24:22 +03:00
commit 7305e54f66
32 changed files with 3612 additions and 0 deletions
+152
View File
@@ -0,0 +1,152 @@
from database.connection import get_db
def normalize_arabic(text: str) -> str:
"""Normalize Arabic text for search: collapse alef variants, ta marbuta, alef maqsura."""
if not text:
return text
for ch in "أإآ":
text = text.replace(ch, "ا")
text = text.replace("ة", "ه")
text = text.replace("ى", "ي")
return text
def search_persons(query: str) -> list[dict]:
"""Search persons by name (first, family, or father name)."""
norm = normalize_arabic(query.strip())
pattern = f"%{norm}%"
raw_pattern = f"%{query.strip()}%"
with get_db() as conn:
rows = conn.execute(
"""
SELECT p.*,
COUNT(DISTINCT pr.id) AS property_count,
COUNT(DISTINCT d.id) AS document_count,
GROUP_CONCAT(DISTINCT d.search_scope) AS search_scopes
FROM persons p
LEFT JOIN properties pr ON pr.person_id = p.id
LEFT JOIN documents d ON d.person_id = p.id
WHERE p.first_name_norm LIKE ?
OR p.family_name_norm LIKE ?
OR p.father_name LIKE ?
OR p.first_name LIKE ?
OR p.family_name LIKE ?
GROUP BY p.id
ORDER BY
CASE WHEN p.first_name_norm = ? THEN 0
WHEN p.family_name_norm = ? THEN 0
ELSE 1 END,
p.first_name
""",
(pattern, pattern, raw_pattern, raw_pattern, raw_pattern, norm, norm),
).fetchall()
return [dict(r) for r in rows]
def search_properties(
property_number: str = "",
district: str = "",
block: str = "",
) -> list[dict]:
"""Search properties by number, district, or block."""
conditions = []
params = []
if property_number:
conditions.append("pr.property_number LIKE ?")
params.append(f"{property_number}%")
if district:
conditions.append("pr.real_estate_district LIKE ?")
params.append(f"%{district}%")
if block:
conditions.append("pr.block LIKE ?")
params.append(f"%{block}%")
if not conditions:
return []
where = " AND ".join(conditions)
with get_db() as conn:
rows = conn.execute(
f"""
SELECT pr.*,
p.first_name, p.father_name, p.family_name, p.family_origin,
d.search_scope
FROM properties pr
LEFT JOIN persons p ON p.id = pr.person_id
LEFT JOIN documents d ON d.id = pr.document_id
WHERE {where}
ORDER BY pr.real_estate_district, pr.property_number
""",
params,
).fetchall()
results = []
for r in rows:
row_dict = dict(r)
if not row_dict.get("qaza") and row_dict.get("search_scope"):
row_dict["qaza"] = row_dict["search_scope"]
results.append(row_dict)
return results
def get_person_with_properties(person_id: int) -> dict | None:
"""Fetch a person and all their properties."""
with get_db() as conn:
person = conn.execute(
"SELECT * FROM persons WHERE id = ?", (person_id,)
).fetchone()
if not person:
return None
props = conn.execute(
"""
SELECT pr.*, d.image_path, d.id AS document_id, d.search_scope
FROM properties pr
LEFT JOIN documents d ON d.id = pr.document_id
WHERE pr.person_id = ?
ORDER BY pr.real_estate_district, pr.row_order
""",
(person_id,),
).fetchall()
docs = conn.execute(
"SELECT id, image_path, request_number, request_date, status, page_info, search_scope FROM documents WHERE person_id = ?",
(person_id,),
).fetchall()
properties_list = []
doc_ids_with_props = set()
for p in props:
p_dict = dict(p)
if not p_dict.get("qaza") and p_dict.get("search_scope"):
p_dict["qaza"] = p_dict["search_scope"]
properties_list.append(p_dict)
doc_ids_with_props.add(p_dict.get("document_id"))
# Add a "no properties" dummy row for documents that yielded no properties
# so the search request is still recorded in the properties table
docs_list = [dict(d) for d in docs]
for d in docs_list:
if d["id"] not in doc_ids_with_props:
properties_list.append({
"document_id": d["id"],
"party_name": "-",
"property_number": "-",
"section": "-",
"block": "-",
"real_estate_district": "-",
"qaza": d.get("search_scope") or "غير محدد",
"num_shares": "-",
"ownership_type": "لا يملك"
})
return {
"person": dict(person),
"properties": properties_list,
"documents": docs_list,
}