/
baga228
/
project_PIAS
Обзор
Документация
Войти
/
baga228
/
project_PIAS
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
backend/app.py
407 строк
16 KB
baga228
upload files
26 дек 2025, 04:00
26 дек 2025, 04:00
1f57827
Код
Авторство
О чём код?
# ========================================== # АНАЛИЗАТОР ЦЕН НА НЕДВИЖИМОСТЬ РОСТОВА # Главный Flask сервер # ========================================== from flask import Flask, jsonify, request from flask_cors import CORS import sqlite3 import json from datetime import datetime import os from api_handler import search_address_2gis # Импортируем наши модули (создадим их потом) # from database import init_db, add_property, get_all_properties, get_analysis # from analysis import analyze_prices app = Flask(__name__) CORS(app) # Разрешаем запросы с фронтенда # ========================================== # ГЛОБАЛЬНЫЕ ПЕРЕМЕННЫЕ # ========================================== DB_PATH = os.path.join(os.path.dirname(__file__), '..', 'database.db') # ========================================== # ИНИЦИАЛИЗАЦИЯ БД # ========================================== def init_db(): """Создаёт таблицы в БД если их ещё нет""" conn = sqlite3.connect(DB_PATH) cursor = conn.cursor() # Таблица районов Ростова cursor.execute(''' CREATE TABLE IF NOT EXISTS districts ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT UNIQUE NOT NULL, population INTEGER, description TEXT ) ''') # Таблица объектов недвижимости cursor.execute(''' CREATE TABLE IF NOT EXISTS properties ( id INTEGER PRIMARY KEY AUTOINCREMENT, district_id INTEGER NOT NULL, address TEXT NOT NULL, price INTEGER NOT NULL, area REAL NOT NULL, rooms INTEGER, year_built INTEGER, description TEXT, date_added TIMESTAMP DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (district_id) REFERENCES districts(id) ) ''') # Таблица анализа (результаты) cursor.execute(''' CREATE TABLE IF NOT EXISTS analysis_results ( id INTEGER PRIMARY KEY AUTOINCREMENT, district_id INTEGER NOT NULL, avg_price REAL, avg_price_per_sqm REAL, min_price INTEGER, max_price INTEGER, property_count INTEGER, date_analyzed TIMESTAMP DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (district_id) REFERENCES districts(id) ) ''') cursor.execute(''' CREATE TABLE IF NOT EXISTS requests_log ( id INTEGER PRIMARY KEY AUTOINCREMENT, request_type TEXT NOT NULL, parameters TEXT, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ) ''') conn.commit() conn.close() print("✓ База данных инициализирована") # ========================================== # ФУНКЦИИ РАБОТЫ С БД # ========================================== def get_connection(): """Возвращает подключение к БД""" conn = sqlite3.connect(DB_PATH) conn.row_factory = sqlite3.Row # Возвращать результаты как словари return conn def add_district(name, population, description): """Добавляет район в БД""" try: conn = get_connection() cursor = conn.cursor() cursor.execute(''' INSERT INTO districts (name, population, description) VALUES (?, ?, ?) ''', (name, population, description)) conn.commit() conn.close() return True except sqlite3.IntegrityError: return False # Район уже существует def add_property(district_id, address, price, area, rooms, year_built, description): """Добавляет объект недвижимости""" conn = get_connection() cursor = conn.cursor() cursor.execute(''' INSERT INTO properties (district_id, address, price, area, rooms, year_built, description) VALUES (?, ?, ?, ?, ?, ?, ?) ''', (district_id, address, price, area, rooms, year_built, description)) conn.commit() conn.close() def get_all_districts(): """Возвращает все районы""" conn = get_connection() cursor = conn.cursor() cursor.execute('SELECT * FROM districts') districts = [dict(row) for row in cursor.fetchall()] conn.close() return districts def get_properties_by_district(district_id): """Возвращает все объекты в районе""" conn = get_connection() cursor = conn.cursor() cursor.execute(''' SELECT p.*, d.name as district_name FROM properties p JOIN districts d ON p.district_id = d.id WHERE p.district_id = ? ''', (district_id,)) properties = [dict(row) for row in cursor.fetchall()] conn.close() return properties def get_all_properties(): """Возвращает все объекты недвижимости""" conn = get_connection() cursor = conn.cursor() cursor.execute(''' SELECT p.*, d.name as district_name FROM properties p JOIN districts d ON p.district_id = d.id ORDER BY p.date_added DESC ''') properties = [dict(row) for row in cursor.fetchall()] conn.close() return properties def log_request(request_type, parameters=None): """Логирует запрос пользователя в БД""" conn = get_connection() cursor = conn.cursor() cursor.execute(''' INSERT INTO requests_log (request_type, parameters) VALUES (?, ?) ''', ( request_type, json.dumps(parameters, ensure_ascii=False) if parameters else None )) conn.commit() conn.close() # ========================================== # ФУНКЦИИ АНАЛИЗА ДАННЫХ # ========================================== def analyze_district(district_id): """Анализирует цены в районе""" properties = get_properties_by_district(district_id) if not properties: return None prices = [p['price'] for p in properties] areas = [p['area'] for p in properties] # Расчёты avg_price = sum(prices) / len(prices) avg_price_per_sqm = sum([p['price'] / p['area'] for p in properties]) / len(properties) min_price = min(prices) max_price = max(prices) property_count = len(properties) return { 'district_id': district_id, 'avg_price': round(avg_price, 2), 'avg_price_per_sqm': round(avg_price_per_sqm, 2), 'min_price': min_price, 'max_price': max_price, 'property_count': property_count } def get_all_analysis(): """Анализирует все районы""" districts = get_all_districts() analysis = [] for district in districts: result = analyze_district(district['id']) if result: result['district_name'] = district['name'] analysis.append(result) # Сортируем по средней цене (самые дорогие первыми) analysis.sort(key=lambda x: x['avg_price'], reverse=True) return analysis # ========================================== # REST API ENDPOINTS # ========================================== @app.route('/', methods=['GET']) def home(): """Главная страница API""" return jsonify({ 'message': 'Анализатор цен на недвижимость Ростова', 'version': '1.0', 'endpoints': { 'GET /api/districts': 'Получить все районы', 'GET /api/properties': 'Получить все объекты', 'GET /api/properties/<district_id>': 'Получить объекты в районе', 'GET /api/analysis': 'Получить анализ цен', 'POST /api/property': 'Добавить новый объект' } }) @app.route('/api/districts', methods=['GET']) def get_districts(): """Возвращает все районы""" districts = get_all_districts() return jsonify(districts) @app.route('/api/properties', methods=['GET']) def get_properties(): """Возвращает все объекты недвижимости""" properties = get_all_properties() return jsonify(properties) @app.route('/api/properties/<int:district_id>', methods=['GET']) def get_properties_district(district_id): """Возвращает объекты в конкретном районе""" properties = get_properties_by_district(district_id) return jsonify(properties) @app.route('/api/analysis', methods=['GET']) def get_analysis(): """Возвращает анализ цен по районам""" # Логируем запрос анализа log_request( request_type='analysis', parameters={ 'ip': request.remote_addr } ) analysis = get_all_analysis() return jsonify(analysis) @app.route('/api/property', methods=['POST']) def add_new_property(): """Добавляет новый объект недвижимости""" try: data = request.json add_property( district_id=data['district_id'], address=data['address'], price=data['price'], area=data['area'], rooms=data.get('rooms'), year_built=data.get('year_built'), description=data.get('description', '') ) return jsonify({'success': True, 'message': 'Объект добавлен'}), 201 except Exception as e: return jsonify({'success': False, 'error': str(e)}), 400 # ========================================== # ДОБАВЛЕНИЕ ТЕСТОВЫХ ДАННЫХ # ========================================== def load_test_data(): """Загружает тестовые данные о недвижимости Ростова""" # Районы Ростова districts_data = [ ('Центральный', 120000, 'Центр города, развитая инфраструктура'), ('Советский', 95000, 'Деловой район, офисные здания'), ('Октябрьский', 110000, 'Жилой район с парками'), ('Кировский', 85000, 'Спальный район, тихо'), ('Ленинский', 100000, 'Развитая транспортная сеть'), ('Краснодарский', 75000, 'Новостройки, развивающийся район') ] for name, pop, desc in districts_data: add_district(name, pop, desc) # Объекты недвижимости (тестовые данные) properties_data = [ # Центральный район (1, 'ул. Большая Садовая, д. 45', 4500000, 65, 3, 1995, 'Уютная трёхкомнатная квартира'), (1, 'ул. Пушкинская, д. 120', 5200000, 75, 3, 2005, 'Евроремонт, панорамный вид'), (1, 'пр. Чехова, д. 88', 4800000, 70, 3, 2010, 'Близко к центру'), (1, 'ул. Советская, д. 50', 5500000, 85, 4, 2015, 'Новостройка, все удобства'), # Советский район (2, 'ул. Ломоносова, д. 67', 3800000, 60, 2, 2000, 'Удачное расположение'), (2, 'пр. Ворошиловский, д. 155', 4200000, 70, 3, 2008, 'Рядом метро'), (2, 'ул. Королькова, д. 89', 3950000, 62, 2, 2005, 'Отличное состояние'), # Октябрьский район (3, 'ул. Зорге, д. 34', 3200000, 55, 2, 1998, 'Рядом парк им. Октября'), (3, 'ул. Ленина, д. 123', 3500000, 60, 2, 2002, 'Спокойный район'), (3, 'пр. Стачки, д. 78', 3800000, 65, 3, 2010, 'Развитая инфраструктура'), # Кировский район (4, 'ул. Вилюйская, д. 45', 2800000, 50, 1, 1995, 'Доступное жильё'), (4, 'ул. Красносельская, д. 67', 3100000, 55, 2, 2000, 'Тихий район'), (4, 'ул. Чайковского, д. 30', 2950000, 52, 2, 1998, 'Рядом школы'), # Ленинский район (5, 'ул. Комсомольская, д. 98', 3400000, 60, 2, 2005, 'Транспортная развязка'), (5, 'пр. Соколова, д. 145', 3600000, 65, 3, 2010, 'Близко до центра'), (5, 'ул. Беркутова, д. 56', 3300000, 58, 2, 2003, 'Спокойно'), # Краснодарский район (6, 'ул. Красносельская, д. 200', 2500000, 48, 1, 2015, 'Новостройка'), (6, 'ул. Огородная, д. 89', 2700000, 52, 2, 2018, 'Современный комплекс'), (6, 'ул. Социалистическая, д. 145', 2600000, 50, 2, 2016, 'Экологичный район') ] for district_id, address, price, area, rooms, year, desc in properties_data: add_property(district_id, address, price, area, rooms, year, desc) print("✓ Тестовые данные загружены") # ========================================== # АПИ ДОБАВЛЕНИЕ # ========================================== @app.route('/api/geocode', methods=['GET']) def geocode_address(): """Получить координаты адреса через 2GIS API""" address = request.args.get('address') if not address: return jsonify({'success': False, 'error': 'address parameter is required'}), 400 try: # Вызываем 2GIS API data = search_address_2gis(address) # Пытаемся вытащить координаты из ответа result = data.get('result', {}) items = result.get('items', []) if not items: return jsonify({'success': False, 'error': 'address not found in 2GIS'}), 404 # Берём первый найденный объект item = items[0] point = item.get('point', {}) lat = point.get('lat') lon = point.get('lon') return jsonify({ 'success': True, 'address': address, 'lat': lat, 'lon': lon, 'raw': item # можно убрать, если слишком много данных }) except Exception as e: return jsonify({'success': False, 'error': str(e)}), 500 # ========================================== # ЗАПУСК ПРИЛОЖЕНИЯ # ========================================== if __name__ == '__main__': init_db() conn = get_connection() cursor = conn.cursor() cursor.execute('SELECT COUNT(*) FROM districts') count = cursor.fetchone()[0] conn.close() if count == 0: load_test_data() print("✓ Сервер запускается на http://localhost:5000") app.run(debug=True, host='0.0.0.0', port=5000)