Browse Source

adding categories

master
Mohsen Taba 4 months ago
parent
commit
9151d06aa2
  1. 175
      apps/account/management/commands/create_geonames_table.py
  2. 4
      apps/account/views/user.py
  3. 581
      apps/hadis/management/commands/seed_category_data.py
  4. 6
      city_detection_ip.py
  5. 4
      entrypoint.sh
  6. 103
      test_mixmind_coordinates.py
  7. 227583
      utils/geonames_data/cities500.txt
  8. BIN
      utils/geonames_data/cities500.zip

175
apps/account/management/commands/create_geonames_table.py

@ -0,0 +1,175 @@
import os
import csv
import zipfile
import requests
from pathlib import Path
from django.core.management.base import BaseCommand, CommandError
from django.db import connection
class Command(BaseCommand):
help = 'Create and populate geonames_city table with GeoNames data'
def add_arguments(self, parser):
parser.add_argument(
'--force',
action='store_true',
help='Force recreation of table even if it exists',
)
parser.add_argument(
'--skip-download',
action='store_true',
help='Skip downloading data, use existing files',
)
def handle(self, *args, **options):
self.stdout.write('Creating geonames_city table...')
# Create table
with connection.cursor() as cursor:
if options['force']:
cursor.execute('DROP TABLE IF EXISTS geonames_city')
cursor.execute('''
CREATE TABLE IF NOT EXISTS geonames_city (
id SERIAL PRIMARY KEY,
geonameid INTEGER,
name VARCHAR(200),
asciiname VARCHAR(200),
alternatenames TEXT,
latitude DECIMAL(10, 7),
longitude DECIMAL(10, 7),
feature_class CHAR(1),
feature_code VARCHAR(10),
country_code CHAR(2),
cc2 VARCHAR(200),
admin1_code VARCHAR(20),
admin2_code VARCHAR(80),
admin3_code VARCHAR(20),
admin4_code VARCHAR(20),
population BIGINT,
elevation INTEGER,
dem INTEGER,
timezone VARCHAR(40),
modification_date DATE
)
''')
# Create indexes for better performance
cursor.execute('CREATE INDEX IF NOT EXISTS idx_geonames_city_coords ON geonames_city (latitude, longitude)')
cursor.execute('CREATE INDEX IF NOT EXISTS idx_geonames_city_country ON geonames_city (country_code)')
cursor.execute('CREATE INDEX IF NOT EXISTS idx_geonames_city_feature ON geonames_city (feature_class)')
cursor.execute('CREATE INDEX IF NOT EXISTS idx_geonames_city_population ON geonames_city (population)')
self.stdout.write(self.style.SUCCESS('Table created successfully'))
if not options['skip_download']:
self.download_and_import_data()
else:
self.stdout.write('Skipping download, using existing data...')
def download_and_import_data(self):
"""Download and import GeoNames cities data"""
self.stdout.write('Downloading GeoNames cities data...')
# Create data directory
data_dir = Path('utils/geonames_data')
data_dir.mkdir(exist_ok=True)
# Download cities500.zip (cities with population > 500)
url = 'https://download.geonames.org/export/dump/cities500.zip'
zip_path = data_dir / 'cities500.zip'
try:
response = requests.get(url, stream=True)
response.raise_for_status()
with open(zip_path, 'wb') as f:
for chunk in response.iter_content(chunk_size=8192):
f.write(chunk)
self.stdout.write('Download completed')
# Extract zip file
with zipfile.ZipFile(zip_path, 'r') as zip_ref:
zip_ref.extractall(data_dir)
# Import data
self.import_cities_data(data_dir / 'cities500.txt')
except Exception as e:
raise CommandError(f'Failed to download/import data: {e}')
def import_cities_data(self, txt_file):
"""Import cities data from GeoNames text file"""
self.stdout.write(f'Importing data from {txt_file}...')
if not txt_file.exists():
raise CommandError(f'File {txt_file} does not exist')
batch_size = 1000
batch = []
with open(txt_file, 'r', encoding='utf-8') as f:
for line_num, line in enumerate(f, 1):
if line_num % 10000 == 0:
self.stdout.write(f'Processing line {line_num}...')
fields = line.strip().split('\t')
if len(fields) < 19:
continue
try:
# Parse the GeoNames format
geonameid = int(fields[0])
name = fields[1][:200] if fields[1] else ''
asciiname = fields[2][:200] if fields[2] else ''
alternatenames = fields[3] if fields[3] else ''
latitude = float(fields[4])
longitude = float(fields[5])
feature_class = fields[6]
feature_code = fields[7]
country_code = fields[8][:2] if fields[8] else ''
cc2 = fields[9] if fields[9] else ''
admin1_code = fields[10] if fields[10] else ''
admin2_code = fields[11] if fields[11] else ''
admin3_code = fields[12] if fields[12] else ''
admin4_code = fields[13] if fields[13] else ''
population = int(fields[14]) if fields[14] and fields[14] != '0' else 0
elevation = int(fields[15]) if fields[15] else None
dem = int(fields[16]) if fields[16] else None
timezone = fields[17] if fields[17] else ''
modification_date = fields[18] if fields[18] else None
batch.append((
geonameid, name, asciiname, alternatenames, latitude, longitude,
feature_class, feature_code, country_code, cc2, admin1_code,
admin2_code, admin3_code, admin4_code, population, elevation,
dem, timezone, modification_date
))
if len(batch) >= batch_size:
self.insert_batch(batch)
batch = []
except (ValueError, IndexError) as e:
self.stdout.write(self.style.WARNING(f'Error parsing line {line_num}: {e}'))
continue
# Insert remaining records
if batch:
self.insert_batch(batch)
self.stdout.write(self.style.SUCCESS('Data import completed'))
def insert_batch(self, batch):
"""Insert a batch of records into the database"""
with connection.cursor() as cursor:
cursor.executemany('''
INSERT INTO geonames_city (
geonameid, name, asciiname, alternatenames, latitude, longitude,
feature_class, feature_code, country_code, cc2, admin1_code,
admin2_code, admin3_code, admin4_code, population, elevation,
dem, timezone, modification_date
) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)
''', batch)

4
apps/account/views/user.py

@ -31,7 +31,6 @@ from utils import send_email, is_valid_email
from config.settings import base as settings
from apps.account.permissions import IsActiveUser
from apps.account.doc import *
logger = logging.getLogger(__name__)
@ -390,11 +389,12 @@ class UserLoginView(CreateAPIView):
else:
ip = request.META.get('REMOTE_ADDR')
return ip
#
def create(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
data = serializer.data
# u = User.objects.get(id = username=request.data['email'] )
user = authenticate(request, username=request.data['email'], password=data['password'])
if not user:
raise ValidationError({"email": "Unable to log in with provided credentials."})

581
apps/hadis/management/commands/seed_category_data.py

@ -1,497 +1,154 @@
"""
Django management command to seed mock HadisCategory trees and new Hadis
for history, fatwa and quote sources.
Place this file in: hadis/management/commands/seed_categories.py
Usage:
python manage.py seed_categories
python manage.py seed_categories --clear
"""
import random
from django.core.management.base import BaseCommand
from django.utils.text import slugify
from django.conf import settings
from apps.hadis.models.category import HadisCategory, HadisSect
from apps.hadis.models.hadis import Hadis, HadisStatus, HadisTag
from django.db import transaction
# Adjust the import path if your app name is different
from apps.hadis.models import Hadis, HadisCategory, HadisSect
class Command(BaseCommand):
help = "Seed HadisCategory trees (history, fatwa, quote) and new Hadis connected to them."
def add_arguments(self, parser):
parser.add_argument(
'--clear',
action='store_true',
help='Clear ONLY categories & hadises created by this command (history/fatwa/quote).',
)
help = 'Seeds HadisCategories with rich Islamic data and links existing Hadiths'
def handle(self, *args, **options):
if options['clear']:
self._clear_seeded_data()
self.stdout.write(self.style.WARNING('--- Starting Islamic Category Seeding ---'))
# Ensure sects exist
try:
shia_sect = HadisSect.objects.get(sect_type=HadisSect.SectType.SHIA)
except HadisSect.DoesNotExist:
shia_sect = HadisSect.objects.create(
# 1. Fetch or Create Sects
shia_sect, _ = HadisSect.objects.get_or_create(
sect_type=HadisSect.SectType.SHIA,
title="Shia",
description="Default Shia sect"
defaults={'title': [{'text': 'Shia (Twelver)', 'language_code': 'en'}], 'is_active': True}
)
try:
sunni_sect = HadisSect.objects.get(sect_type=HadisSect.SectType.SUNNI)
except HadisSect.DoesNotExist:
sunni_sect = HadisSect.objects.create(
sunni_sect, _ = HadisSect.objects.get_or_create(
sect_type=HadisSect.SectType.SUNNI,
title="Sunni",
description="Default Sunni sect"
)
# Optional: ensure a couple of HadisStatus entries exist
default_status, _ = HadisStatus.objects.get_or_create(
title="Authentic / Accepted",
defaults={"color": HadisStatus.ColorChoices.GREEN, "order": 1},
)
weak_status, _ = HadisStatus.objects.get_or_create(
title="Weak / Needs Review",
defaults={"color": HadisStatus.ColorChoices.YELLOW, "order": 2},
)
# Ensure some tags exist
tags = self._ensure_tags()
# 1) Seed HISTORY categories & hadises
self.stdout.write(self.style.SUCCESS("\n📚 Creating HISTORY categories and hadiths..."))
self._seed_history_tree(sunni_sect, default_status, tags)
# 2) Seed FATWA categories & hadises
self.stdout.write(self.style.SUCCESS("\n📜 Creating FATWA categories and hadiths..."))
self._seed_fatwa_tree(sunni_sect, default_status, weak_status, tags)
# 3) Seed QUOTE categories & hadises
self.stdout.write(self.style.SUCCESS("\n💬 Creating QUOTE categories and hadiths..."))
self._seed_quote_tree(shia_sect, default_status, tags)
self._print_summary()
# ------------------------------------------------------------------ #
# Helpers
# ------------------------------------------------------------------ #
def _clear_seeded_data(self):
"""
Clear only categories with source_type in (history, fatwa, quote)
and hadises that are attached to those categories.
"""
self.stdout.write(self.style.WARNING("Clearing previously seeded history/fatwa/quote data..."))
leaf_categories = HadisCategory.objects.filter(
source_type__in=[
HadisCategory.SourceType.HISTORY,
HadisCategory.SourceType.FATWA,
HadisCategory.SourceType.QUOTE,
]
)
Hadis.objects.filter(category__in=leaf_categories).delete()
# Delete categories (MPTT will handle tree structure)
HadisCategory.objects.filter(
source_type__in=[
HadisCategory.SourceType.HISTORY,
HadisCategory.SourceType.FATWA,
HadisCategory.SourceType.QUOTE,
defaults={'title': [{'text': 'Sunni (Ahlus Sunnah)', 'language_code': 'en'}], 'is_active': True}
)
# 2. Rich Islamic Data Dictionary
ISLAMIC_DATA = {
HadisCategory.SourceType.QURAN: {
HadisSect.SectType.SHIA: [
("Quranic Sciences", "Thematic Exegesis", ["Verses of Wilayah", "Ahlulbayt in Quran", "Moral Teachings in Surah Yusuf"]),
("Tafsir Studies", "Interpretation Principles", ["Tafsir al-Mizan Topics", "Esoteric Meanings (Ta'wil)"])
],
HadisSect.SectType.SUNNI: [
("Quranic Studies", "Tafsir Methodology", ["Asbab al-Nuzul", "Stories of the Prophets", "Legislative Verses (Ahkam)"]),
("Recitation (Tajweed)", "Qira'at", ["Hafs an Asim", "Warsh an Nafi", "Rules of Nun Sakinah"])
]
).delete()
self.stdout.write(self.style.SUCCESS("✓ Cleared seeded history/fatwa/quote categories and hadiths."))
def _ensure_tags(self):
base_titles = [
"history",
"biography",
"battle",
"ethics",
"jurisprudence",
"family",
"wisdom",
"short quote",
]
tags = []
for title in base_titles:
tag, _ = HadisTag.objects.get_or_create(title=title)
tags.append(tag)
return tags
# ---------------- HISTORY ---------------- #
def _seed_history_tree(self, sect, default_status, tags):
"""
Create a small HISTORY tree:
History of Islam
Early Caliphate (leaf)
Battles (leaf)
And add several hadith-like historical reports.
"""
root, _ = HadisCategory.objects.get_or_create(
sect=sect,
source_type=HadisCategory.SourceType.HISTORY,
title="History of Islam",
defaults={
"description": "High-level historical themes related to early Islamic history.",
"order": 1,
},
)
early_caliphate, _ = HadisCategory.objects.get_or_create(
sect=sect,
source_type=HadisCategory.SourceType.HISTORY,
parent=root,
title="Early Caliphate",
defaults={
"description": "Events and reports from the period of the first caliphs.",
"order": 1,
},
)
battles, _ = HadisCategory.objects.get_or_create(
sect=sect,
source_type=HadisCategory.SourceType.HISTORY,
parent=root,
title="Battles and Expeditions",
defaults={
"description": "Key battles and expeditions in early Islamic history.",
"order": 2,
},
)
# Create some historical “hadith” entries (reports)
# For simplicity, use new numbers starting from 1900+
self._create_hadis(
number=1900,
category=early_caliphate,
title="The Consultation of the Companions",
text=(
"It is reported in historical works that the companions would gather and "
"consult one another regarding major affairs of the community."
),
translation_en=(
"Historical reports mention that the companions would consult one another "
"on major communal matters."
),
status=default_status,
tags=[t for t in tags if t.title in ["history", "biography"]],
)
self._create_hadis(
number=1901,
category=early_caliphate,
title="Establishment of the Public Treasury",
text=(
"Some historians narrate that during the early caliphate a public treasury "
"was organized to administer charity and public funds."
),
translation_en=(
"Historical sources state that a public treasury was set up to manage "
"charity and communal funds."
),
status=default_status,
tags=[t for t in tags if t.title in ["history", "jurisprudence"]],
)
self._create_hadis(
number=1910,
category=battles,
title="Report of the Battle Preparations",
text=(
"Chronicles record that the believers prepared carefully before major battles, "
"ensuring justice and discipline in their ranks."
),
translation_en=(
"Historical chronicles record careful preparations and discipline before major battles."
),
status=default_status,
tags=[t for t in tags if t.title in ["history", "battle"]],
)
self._create_hadis(
number=1911,
category=battles,
title="Mercy Shown After Victory",
text=(
"Historical narrations mention that after some victories, clemency and mercy were "
"shown to prisoners and civilians."
),
translation_en=(
"Historical narrations mention mercy and clemency after some military victories."
),
status=default_status,
tags=[t for t in tags if t.title in ["history", "ethics"]],
)
# ---------------- FATWA ---------------- #
def _seed_fatwa_tree(self, sect, default_status, weak_status, tags):
"""
Create a FATWA tree like:
Contemporary Fatwas
Worship (leaf)
Family Issues (leaf)
Plus a second root:
Financial Fatwas
Trade and Contracts (leaf)
"""
root_contemporary, _ = HadisCategory.objects.get_or_create(
sect=sect,
source_type=HadisCategory.SourceType.FATWA,
title="Contemporary Fatwas",
defaults={
"description": "Modern juristic responses to contemporary questions.",
"order": 1,
},
)
worship, _ = HadisCategory.objects.get_or_create(
sect=sect,
source_type=HadisCategory.SourceType.FATWA,
parent=root_contemporary,
title="Worship and Rituals",
defaults={
"description": "Fatwas about prayer, fasting and other acts of worship.",
"order": 1,
},
)
family_issues, _ = HadisCategory.objects.get_or_create(
sect=sect,
source_type=HadisCategory.SourceType.FATWA,
parent=root_contemporary,
title="Family Issues",
defaults={
"description": "Fatwas regarding marriage, divorce and family obligations.",
"order": 2,
HadisCategory.SourceType.HADITH: {
HadisSect.SectType.SHIA: [
("The Four Books", "Usul al-Kafi", ["Book of Intellect", "Book of Divine Proof", "Book of Belief"]),
("Nahj al-Balagha", "Sermons of Imam Ali", ["The Shiqshiqiyyah Sermon", "Letter to Malik al-Ashtar", "Aphorisms of Wisdom"])
],
HadisSect.SectType.SUNNI: [
("The Six Books", "Sahih al-Bukhari", ["Book of Revelation", "Book of Belief", "Book of Knowledge"]),
("Sunan Collections", "Sunan Abu Dawood", ["Book of Prayer", "Book of Zakat", "Book of Jihad"])
]
},
)
financial_root, _ = HadisCategory.objects.get_or_create(
sect=sect,
source_type=HadisCategory.SourceType.FATWA,
title="Financial Fatwas",
defaults={
"description": "Juristic rulings about trade, contracts and modern finance.",
"order": 2,
HadisCategory.SourceType.HISTORY: {
HadisSect.SectType.SHIA: [
("Life of the Infallibles", "The Tragedy of Karbala", ["Day of Ashura", "The Sermon of Zaynab (SA)", "Journey of Captives"]),
("Occultation (Ghaybah)", "Major Occultation", ["Signs of Reappearance", "Duties of Believers", "Deputies of the Imam"])
],
HadisSect.SectType.SUNNI: [
("Seerah of the Prophet", "Meccan Period", ["The First Revelation", "Migration to Abyssinia", "The Year of Sorrow"]),
("The Rashidun Caliphate", "Conquests and Expansion", ["Caliphate of Abu Bakr", "Caliphate of Umar", "Battles of Yarmouk"])
]
},
)
trade_contracts, _ = HadisCategory.objects.get_or_create(
sect=sect,
source_type=HadisCategory.SourceType.FATWA,
parent=financial_root,
title="Trade and Contracts",
defaults={
"description": "Fatwas related to buying, selling and contractual agreements.",
"order": 1,
HadisCategory.SourceType.FATWA: {
HadisSect.SectType.SHIA: [
("Jurisprudence (Fiqh)", "Acts of Worship", ["Rules of Taqlid", "Khums and Zakat", "Rules of Salatul Layl"]),
("Modern Legal Issues", "Medical Jurisprudence", ["Organ Donation", "Assisted Reproduction", "Gender Reassignment"])
],
HadisSect.SectType.SUNNI: [
("Fiqh Schools", "Shafi'i School", ["Rules of Prayer", "Inheritance Laws", "Marriage Contracts"]),
("Contemporary Fatawa", "Islamic Finance", ["Prohibition of Riba", "Islamic Banking Principles", "Crypto-currency Rulings"])
]
},
)
HadisCategory.SourceType.QUOTE: {
HadisSect.SectType.SHIA: [
("Wisdom of the Imams", "Supplications (Du'a)", ["Du'a Kumayl Themes", "Whispered Prayers (Munajat)", "Du'a Arafah"]),
("Mysticism (Irfan)", "Spiritual Wayfaring", ["Combat with the Self", "Degrees of Piety", "Love for the Divine"])
],
HadisSect.SectType.SUNNI: [
("Sayings of the Companions", "Wisdom of Abu Bakr & Umar", ["Justice in Governance", "Fear of Allah", "Humility"]),
("Sufi Wisdom", "Spiritual Purification", ["Tazkiyat al-Nafs", "Remembrance of Death", "Reliance on Allah (Tawakkul)"])
]
}
}
# Create some “fatwa-style” hadith entries (answers / rulings)
self._create_hadis(
number=1920,
category=worship,
title="Fatwa on Combining Prayers While Traveling",
text=(
"A contemporary juristic council has ruled that combining prayers while traveling "
"is permitted when hardship is present, following classical precedents."
),
translation_en=(
"A modern fatwa allows combining prayers during travel where hardship exists, "
"based on earlier jurisprudence."
),
status=default_status,
tags=[t for t in tags if t.title in ["jurisprudence", "ethics"]],
)
created_leaves = []
self._create_hadis(
number=1921,
category=worship,
title="Fatwa on Using Local Calculations for Prayer Times",
text=(
"Modern scholars have issued fatwas permitting the use of accurate astronomical "
"calculations for determining prayer times."
),
translation_en=(
"Contemporary fatwas permit using precise astronomical calculations for prayer times."
),
status=weak_status,
tags=[t for t in tags if t.title in ["jurisprudence", "knowledge"]],
)
with transaction.atomic():
self.stdout.write('Building Rich Islamic Category Tree...')
self._create_hadis(
number=1930,
category=family_issues,
title="Fatwa on Upholding Family Ties",
text=(
"A fatwa committee emphasized that maintaining family ties is obligatory and that "
"cutting off relatives without valid reason is sinful."
),
translation_en=(
"A fatwa stresses that keeping family ties is obligatory and severing them without cause is sinful."
),
status=default_status,
tags=[t for t in tags if t.title in ["family", "ethics"]],
)
for source_code, sect_data in ISLAMIC_DATA.items():
for sect_type_enum, categories_list in sect_data.items():
self._create_hadis(
number=1940,
category=trade_contracts,
title="Fatwa on Transparent Business Contracts",
text=(
"Scholars have ruled that contracts must be transparent and free from deception in order "
"to be valid in Islamic law."
),
translation_en=(
"A modern fatwa requires transparency and absence of deception in business contracts."
),
status=default_status,
tags=[t for t in tags if t.title in ["business", "jurisprudence"]] if any(
t.title == "business" for t in tags
) else [t for t in tags if t.title in ["jurisprudence"]],
)
current_sect = shia_sect if sect_type_enum == HadisSect.SectType.SHIA else sunni_sect
# ---------------- QUOTE ---------------- #
for root_title, sub_title, leaf_titles in categories_list:
def _seed_quote_tree(self, sect, default_status, tags):
"""
Create a QUOTE tree:
Wisdom Quotes
Short Wisdom (leaf)
On Knowledge (leaf)
"""
root, _ = HadisCategory.objects.get_or_create(
sect=sect,
source_type=HadisCategory.SourceType.QUOTE,
title="Wisdom Quotes",
# --- Level 1: Root ---
# ADDED: description to defaults
root_cat, _ = HadisCategory.objects.get_or_create(
sect=current_sect,
source_type=source_code,
title=[{'text': root_title, 'language_code': 'en'}],
defaults={
"description": "Short wise sayings and inspirational quotes.",
"order": 1,
},
'parent': None,
'description': [{'text': f'Root category for {root_title}', 'language_code': 'en'}]
}
)
short_wisdom, _ = HadisCategory.objects.get_or_create(
sect=sect,
source_type=HadisCategory.SourceType.QUOTE,
parent=root,
title="Short Wisdom",
# --- Level 2: Sub ---
# ADDED: description to defaults
sub_cat, _ = HadisCategory.objects.get_or_create(
sect=current_sect,
source_type=source_code,
title=[{'text': sub_title, 'language_code': 'en'}],
defaults={
"description": "Very short, memorable quotes on character and behavior.",
"order": 1,
},
)
on_knowledge, _ = HadisCategory.objects.get_or_create(
sect=sect,
source_type=HadisCategory.SourceType.QUOTE,
parent=root,
title="On Knowledge",
'parent': root_cat,
'description': [{'text': f'Sub-category for {sub_title}', 'language_code': 'en'}]
}
)
# --- Level 3: Leaves ---
for leaf_title in leaf_titles:
# ADDED: description to defaults
leaf_cat, _ = HadisCategory.objects.get_or_create(
sect=current_sect,
source_type=source_code,
title=[{'text': leaf_title, 'language_code': 'en'}],
defaults={
"description": "Quotes emphasizing the virtue of knowledge and learning.",
"order": 2,
},
'parent': sub_cat,
'description': [{'text': f'Leaf category for {leaf_title}', 'language_code': 'en'}]
}
)
created_leaves.append(leaf_cat)
self.stdout.write(f"Created Leaf: {leaf_title} ({current_sect.get_title('en')})")
# Short quote-style entries
self._create_hadis(
number=1950,
category=short_wisdom,
title="The Measure of a Heart",
text="It is said: The worth of a person is in what their heart carries of mercy and truth.",
translation_en="It is said: A person’s value is measured by the mercy and truth in their heart.",
status=default_status,
tags=[t for t in tags if t.title in ["wisdom", "short quote", "ethics"]],
)
self._create_hadis(
number=1951,
category=short_wisdom,
title="Silence and Wisdom",
text="One of the wise said: Many people would be considered wise if they knew when to remain silent.",
translation_en="A wise saying: Many would be counted wise if they knew when to be silent.",
status=default_status,
tags=[t for t in tags if t.title in ["wisdom", "short quote"]],
)
self._create_hadis(
number=1960,
category=on_knowledge,
title="Seeking Knowledge as Light",
text="It is narrated from the scholars: Knowledge is a light that guides the heart towards what benefits it.",
translation_en="Scholars say: Knowledge is a light guiding the heart to what benefits it.",
status=default_status,
tags=[t for t in tags if t.title in ["knowledge", "wisdom"]],
)
self.stdout.write(self.style.SUCCESS(f'Successfully structured {len(created_leaves)} leaf categories.'))
self._create_hadis(
number=1961,
category=on_knowledge,
title="Learning until the End",
text="One sage said: Continue to seek knowledge until the last day of your life, for ignorance is a darkness.",
translation_en="A sage said: Seek knowledge until your last day, for ignorance is darkness.",
status=default_status,
tags=[t for t in tags if t.title in ["knowledge", "wisdom"]],
)
# ---------------- utility to create Hadis ---------------- #
# 3. Connect Hadiths to Leaves
all_hadiths = list(Hadis.objects.all())
total_hadiths = len(all_hadiths)
def _create_hadis(self, number, category, title, text, translation_en, status, tags):
"""
Create or update a Hadis with the given number and category.
Translation uses {'en': '...'} structure as requested.
"""
hadis, created = Hadis.objects.get_or_create(
number=number,
category=category,
defaults={
"title": title,
"title_narrator": None,
"description": "",
"text": text,
"translation": {"en": translation_en},
"status": True,
"hadis_status": status,
"hadis_status_text": status.title if status else "",
"address": "",
"links": {},
"explanation": "",
},
)
if not created:
hadis.title = title
hadis.text = text
hadis.translation = {"en": translation_en}
hadis.hadis_status = status
hadis.hadis_status_text = status.title if status else ""
hadis.category = category
hadis.save()
if total_hadiths < 3:
self.stdout.write(self.style.ERROR('Not enough Hadiths in DB. Please seed Hadiths first (need at least 3).'))
return
if tags:
hadis.tags.set(tags)
return hadis
self.stdout.write(f'Distributing {total_hadiths} Hadiths among leaves...')
def _print_summary(self):
from apps.hadis.models import HadisCategory, Hadis
random.shuffle(all_hadiths)
hadith_index = 0
self.stdout.write("\n" + "=" * 70)
self.stdout.write(self.style.SUCCESS("CATEGORY & HADIS SUMMARY"))
self.stdout.write("=" * 70)
for leaf in created_leaves:
if hadith_index + 3 > total_hadiths:
hadith_index = 0
history_count = HadisCategory.objects.filter(source_type=HadisCategory.SourceType.HISTORY).count()
fatwa_count = HadisCategory.objects.filter(source_type=HadisCategory.SourceType.FATWA).count()
quote_count = HadisCategory.objects.filter(source_type=HadisCategory.SourceType.QUOTE).count()
batch = all_hadiths[hadith_index : hadith_index + 3]
hadith_index += 3
history_hadis = Hadis.objects.filter(category__source_type=HadisCategory.SourceType.HISTORY).count()
fatwa_hadis = Hadis.objects.filter(category__source_type=HadisCategory.SourceType.FATWA).count()
quote_hadis = Hadis.objects.filter(category__source_type=HadisCategory.SourceType.QUOTE).count()
for hadis_obj in batch:
hadis_obj.category = leaf
hadis_obj.save()
self.stdout.write(f"📂 HISTORY categories: {history_count} | hadith-like records: {history_hadis}")
self.stdout.write(f"📂 FATWA categories: {fatwa_count} | fatwa records: {fatwa_hadis}")
self.stdout.write(f"📂 QUOTE categories: {quote_count} | quote records: {quote_hadis}")
self.stdout.write("=" * 70 + "\n")
self.stdout.write(self.style.SUCCESS('--- Islamic Category Seeding Complete ---'))

6
city_detection_ip.py

@ -210,12 +210,16 @@ def get_location_by_coordinates(lat, lon):
Try optimized first, fallback to original if needed.
"""
# Try optimized version first
print('lat:',lat ,'lon:', lon)
result = get_location_by_coordinates_optimized(lat, lon)
print('optimize:',result)
# If optimized fails, use original as fallback
if result is None:
result = get_location_by_coordinates_original(lat, lon)
print ('original',result)
print('out',result)
return result
def get_location_by_ip(ip):

4
entrypoint.sh

@ -9,6 +9,6 @@ python manage.py collectstatic --noinput
# python manage.py populate_book_reference
# python manage.py populate_refrence_images
# python manage.py populate_article
python manage.py populate_books
# python manage.py populate_books
python manage.py seed_category_data
exec "$@"

103
test_mixmind_coordinates.py

@ -0,0 +1,103 @@
#!/usr/bin/env python
import os
import django
from pathlib import Path
# Setup Django
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'config.settings.develop')
django.setup()
from city_detection_ip import CITY_DB_PATH
import geoip2.database
def test_maxmind_capabilities():
"""Test what the MaxMind GeoLite2 database can actually do"""
if not CITY_DB_PATH.exists():
print(f"❌ Database file not found: {CITY_DB_PATH}")
return
print(f"✅ Found MaxMind database: {CITY_DB_PATH}")
try:
with geoip2.database.Reader(CITY_DB_PATH) as reader:
# Show what methods are available on the reader
print("\n📋 Available methods on MaxMind reader:")
methods = [method for method in dir(reader) if not method.startswith('_')]
for method in methods:
print(f" - {method}")
# Test IP lookup (this works)
print("\n🧪 Testing IP lookup (should work):")
try:
response = reader.city('8.8.8.8') # Google DNS IP
print(f" IP 8.8.8.8 → City: {response.city.name}, Country: {response.country.iso_code}")
except Exception as e:
print(f" ❌ IP lookup failed: {e}")
# Try to find coordinate-based methods (these won't exist)
print("\n🔍 Checking for coordinate-based methods:")
coord_methods = ['city_by_coords', 'location', 'reverse', 'geocode', 'coordinates', 'latlon']
found_coord_methods = [m for m in methods if any(coord in m.lower() for coord in coord_methods)]
if found_coord_methods:
print(f" ⚠️ Found potential coordinate methods: {found_coord_methods}")
for method in found_coord_methods:
try:
method_obj = getattr(reader, method)
print(f" - {method}: {method_obj}")
except Exception as e:
print(f" - {method}: Error - {e}")
else:
print(" ✅ No coordinate-based methods found (as expected)")
# Demonstrate the limitation
print("\n🚫 Demonstrating the limitation:")
print(" MaxMind databases only support IP → Location lookups")
print(" They do NOT support Coordinates → Location lookups")
print(" For reverse geocoding, you need a spatial database like our geonames_city table")
except Exception as e:
print(f"❌ Error opening database: {e}")
def test_coordinate_lookup_attempt():
"""Try to 'force' a coordinate lookup to show it doesn't work"""
print("\n🎯 Attempting coordinate lookup (this will fail):")
if not CITY_DB_PATH.exists():
return
try:
with geoip2.database.Reader(CITY_DB_PATH) as reader:
# Try various ways to pass coordinates (all will fail)
test_coords = [
"5.161.94.84", # Tehran coordinates
"35.698,51.4115", # String format
[35.698, 51.4115], # List format
]
for coords in test_coords:
try:
# This will raise AttributeError - no such method exists
response = reader.city(coords)
print(f" ❌ Unexpectedly succeeded with {coords}: {response}")
except AttributeError as e:
print(f" ✅ Expected error with {coords}: {e}")
except Exception as e:
print(f" ❓ Unexpected error with {coords}: {e}")
except Exception as e:
print(f"❌ Database error: {e}")
if __name__ == "__main__":
print("🧪 Testing MaxMind GeoLite2 Database Capabilities")
print("=" * 50)
test_maxmind_capabilities()
test_coordinate_lookup_attempt()
print("\n" + "=" * 50)
print("🎯 CONCLUSION:")
print("MaxMind GeoLite2 databases are for IP geolocation only.")
print("Use the geonames_city table for coordinate-based reverse geocoding.")

227583
utils/geonames_data/cities500.txt
File diff suppressed because it is too large
View File

BIN
utils/geonames_data/cities500.zip

Loading…
Cancel
Save