📞 Contacts Manager - Murakkab loyiha

Contacts Manager loyihasi

Bu darsda SQLite3 ishlatgan holda to'liq Contacts Manager loyihasini yaratamiz. Bu loyiha murakkab ma'lumotlar bazasi dizayni va ilg'or SQLite3 texnikalarini qo'llaydi.

1. Loyiha struktura

contacts_manager/
├── app.py              # Asosiy Flask app
├── database.py         # Ma'lumotlar bazasi funksiyalari
├── models.py           # Data modellar
├── templates/          # HTML template-lar
│   ├── base.html
│   ├── index.html
│   ├── contact_detail.html
│   ├── add_contact.html
│   └── edit_contact.html
├── static/            # CSS va JS fayllar
│   ├── style.css
│   └── script.js
├── requirements.txt   # Dependencies
└── contacts.db       # SQLite ma'lumotlar bazasi

2. Ma'lumotlar bazasi dizayni

# database.py
import sqlite3
from datetime import datetime
import json

class ContactsDatabase:
    def __init__(self, db_path='contacts.db'):
        self.db_path = db_path
        self.init_database()
    
    def init_database(self):
        """Ma'lumotlar bazasini ishga tushirish"""
        with sqlite3.connect(self.db_path) as conn:
            cursor = conn.cursor()
            
            # Contacts jadvali
            cursor.execute('''
            CREATE TABLE IF NOT EXISTS contacts (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                first_name TEXT NOT NULL,
                last_name TEXT NOT NULL,
                email TEXT UNIQUE,
                phone TEXT,
                company TEXT,
                job_title TEXT,
                notes TEXT,
                avatar_url TEXT,
                is_favorite BOOLEAN DEFAULT 0,
                created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
                updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
            )
            ''')
            
            # Contact groups jadvali
            cursor.execute('''
            CREATE TABLE IF NOT EXISTS contact_groups (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                name TEXT UNIQUE NOT NULL,
                description TEXT,
                color TEXT DEFAULT '#007bff',
                created_at DATETIME DEFAULT CURRENT_TIMESTAMP
            )
            ''')
            
            # Contact group memberships
            cursor.execute('''
            CREATE TABLE IF NOT EXISTS contact_group_memberships (
                contact_id INTEGER,
                group_id INTEGER,
                PRIMARY KEY (contact_id, group_id),
                FOREIGN KEY (contact_id) REFERENCES contacts(id) ON DELETE CASCADE,
                FOREIGN KEY (group_id) REFERENCES contact_groups(id) ON DELETE CASCADE
            )
            ''')
            
            # Contact addresses
            cursor.execute('''
            CREATE TABLE IF NOT EXISTS contact_addresses (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                contact_id INTEGER,
                address_type TEXT DEFAULT 'home',
                street TEXT,
                city TEXT,
                state TEXT,
                postal_code TEXT,
                country TEXT,
                is_primary BOOLEAN DEFAULT 0,
                FOREIGN KEY (contact_id) REFERENCES contacts(id) ON DELETE CASCADE
            )
            ''')
            
            # Contact social media
            cursor.execute('''
            CREATE TABLE IF NOT EXISTS contact_social_media (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                contact_id INTEGER,
                platform TEXT NOT NULL,
                username TEXT NOT NULL,
                url TEXT,
                is_primary BOOLEAN DEFAULT 0,
                FOREIGN KEY (contact_id) REFERENCES contacts(id) ON DELETE CASCADE
            )
            ''')
            
            # Contact tags
            cursor.execute('''
            CREATE TABLE IF NOT EXISTS contact_tags (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                name TEXT UNIQUE NOT NULL,
                color TEXT DEFAULT '#6c757d',
                created_at DATETIME DEFAULT CURRENT_TIMESTAMP
            )
            ''')
            
            # Contact tag assignments
            cursor.execute('''
            CREATE TABLE IF NOT EXISTS contact_tag_assignments (
                contact_id INTEGER,
                tag_id INTEGER,
                PRIMARY KEY (contact_id, tag_id),
                FOREIGN KEY (contact_id) REFERENCES contacts(id) ON DELETE CASCADE,
                FOREIGN KEY (tag_id) REFERENCES contact_tags(id) ON DELETE CASCADE
            )
            ''')
            
            # Contact interactions
            cursor.execute('''
            CREATE TABLE IF NOT EXISTS contact_interactions (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                contact_id INTEGER,
                interaction_type TEXT NOT NULL,
                subject TEXT,
                notes TEXT,
                interaction_date DATETIME DEFAULT CURRENT_TIMESTAMP,
                created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
                FOREIGN KEY (contact_id) REFERENCES contacts(id) ON DELETE CASCADE
            )
            ''')
            
            # Foreign Key yoqish
            cursor.execute('PRAGMA foreign_keys = ON')
            
            # Default groups qo'shish
            cursor.execute('''
            INSERT OR IGNORE INTO contact_groups (name, description, color) VALUES 
            ('Family', 'Oilaviy a''zolar', '#28a745'),
            ('Friends', 'Do''stlar', '#007bff'),
            ('Work', 'Ish do''stlari', '#ffc107'),
            ('Business', 'Biznes sheriklari', '#dc3545')
            ''')
            
            # Default tags qo'shish
            cursor.execute('''
            INSERT OR IGNORE INTO contact_tags (name, color) VALUES 
            ('VIP', '#dc3545'),
            ('Client', '#28a745'),
            ('Supplier', '#007bff'),
            ('Colleague', '#6c757d')
            ''')
            
            conn.commit()
    
    def create_contact(self, first_name, last_name, email=None, phone=None, 
                      company=None, job_title=None, notes=None, avatar_url=None,
                      group_ids=None, tag_ids=None, addresses=None, social_media=None):
        """Yangi contact yaratish"""
        with sqlite3.connect(self.db_path) as conn:
            cursor = conn.cursor()
            
            cursor.execute('''
            INSERT INTO contacts (first_name, last_name, email, phone, company, 
                               job_title, notes, avatar_url) 
            VALUES (?, ?, ?, ?, ?, ?, ?, ?)
            ''', (first_name, last_name, email, phone, company, job_title, notes, avatar_url))
            
            contact_id = cursor.lastrowid
            
            # Groups qo'shish
            if group_ids:
                for group_id in group_ids:
                    cursor.execute('''
                    INSERT INTO contact_group_memberships (contact_id, group_id) 
                    VALUES (?, ?)
                    ''', (contact_id, group_id))
            
            # Tags qo'shish
            if tag_ids:
                for tag_id in tag_ids:
                    cursor.execute('''
                    INSERT INTO contact_tag_assignments (contact_id, tag_id) 
                    VALUES (?, ?)
                    ''', (contact_id, tag_id))
            
            # Addresses qo'shish
            if addresses:
                for address in addresses:
                    cursor.execute('''
                    INSERT INTO contact_addresses (contact_id, address_type, street, 
                                                city, state, postal_code, country, is_primary) 
                    VALUES (?, ?, ?, ?, ?, ?, ?, ?)
                    ''', (contact_id, address.get('type', 'home'), address.get('street'),
                         address.get('city'), address.get('state'), address.get('postal_code'),
                         address.get('country'), address.get('is_primary', 0)))
            
            # Social media qo'shish
            if social_media:
                for social in social_media:
                    cursor.execute('''
                    INSERT INTO contact_social_media (contact_id, platform, username, url, is_primary) 
                    VALUES (?, ?, ?, ?, ?)
                    ''', (contact_id, social.get('platform'), social.get('username'),
                         social.get('url'), social.get('is_primary', 0)))
            
            conn.commit()
            return contact_id
    
    def get_all_contacts(self, search=None, group_id=None, tag_id=None, is_favorite=None):
        """Barcha contactlarni olish"""
        with sqlite3.connect(self.db_path) as conn:
            cursor = conn.cursor()
            
            query = '''
            SELECT c.id, c.first_name, c.last_name, c.email, c.phone, c.company, 
                   c.job_title, c.is_favorite, c.created_at, c.updated_at,
                   GROUP_CONCAT(DISTINCT cg.name) as groups,
                   GROUP_CONCAT(DISTINCT ct.name) as tags
            FROM contacts c
            LEFT JOIN contact_group_memberships cgm ON c.id = cgm.contact_id
            LEFT JOIN contact_groups cg ON cgm.group_id = cg.id
            LEFT JOIN contact_tag_assignments cta ON c.id = cta.contact_id
            LEFT JOIN contact_tags ct ON cta.tag_id = ct.id
            WHERE 1=1
            '''
            
            params = []
            
            if search:
                query += ''' AND (c.first_name LIKE ? OR c.last_name LIKE ? 
                                OR c.email LIKE ? OR c.phone LIKE ? 
                                OR c.company LIKE ?)'''
                search_param = f'%{search}%'
                params.extend([search_param] * 5)
            
            if group_id:
                query += ' AND c.id IN (SELECT contact_id FROM contact_group_memberships WHERE group_id = ?)'
                params.append(group_id)
            
            if tag_id:
                query += ' AND c.id IN (SELECT contact_id FROM contact_tag_assignments WHERE tag_id = ?)'
                params.append(tag_id)
            
            if is_favorite is not None:
                query += ' AND c.is_favorite = ?'
                params.append(is_favorite)
            
            query += ' GROUP BY c.id ORDER BY c.first_name, c.last_name'
            
            cursor.execute(query, params)
            return cursor.fetchall()
    
    def get_contact_by_id(self, contact_id):
        """Contact ma'lumotlarini olish"""
        with sqlite3.connect(self.db_path) as conn:
            cursor = conn.cursor()
            
            # Asosiy ma'lumotlar
            cursor.execute('''
            SELECT * FROM contacts WHERE id = ?
            ''', (contact_id,))
            
            contact = cursor.fetchone()
            
            if not contact:
                return None
            
            # Groups
            cursor.execute('''
            SELECT cg.id, cg.name, cg.color
            FROM contact_groups cg
            INNER JOIN contact_group_memberships cgm ON cg.id = cgm.group_id
            WHERE cgm.contact_id = ?
            ''', (contact_id,))
            groups = cursor.fetchall()
            
            # Tags
            cursor.execute('''
            SELECT ct.id, ct.name, ct.color
            FROM contact_tags ct
            INNER JOIN contact_tag_assignments cta ON ct.id = cta.tag_id
            WHERE cta.contact_id = ?
            ''', (contact_id,))
            tags = cursor.fetchall()
            
            # Addresses
            cursor.execute('''
            SELECT * FROM contact_addresses WHERE contact_id = ? ORDER BY is_primary DESC
            ''', (contact_id,))
            addresses = cursor.fetchall()
            
            # Social media
            cursor.execute('''
            SELECT * FROM contact_social_media WHERE contact_id = ? ORDER BY is_primary DESC
            ''', (contact_id,))
            social_media = cursor.fetchall()
            
            # Interactions
            cursor.execute('''
            SELECT * FROM contact_interactions WHERE contact_id = ? 
            ORDER BY interaction_date DESC LIMIT 10
            ''', (contact_id,))
            interactions = cursor.fetchall()
            
            return {
                'contact': contact,
                'groups': groups,
                'tags': tags,
                'addresses': addresses,
                'social_media': social_media,
                'interactions': interactions
            }
    
    def update_contact(self, contact_id, **kwargs):
        """Contact ma'lumotlarini yangilash"""
        with sqlite3.connect(self.db_path) as conn:
            cursor = conn.cursor()
            
            # Asosiy ma'lumotlarni yangilash
            updates = []
            params = []
            
            for field in ['first_name', 'last_name', 'email', 'phone', 'company', 
                         'job_title', 'notes', 'avatar_url', 'is_favorite']:
                if field in kwargs:
                    updates.append(f'{field} = ?')
                    params.append(kwargs[field])
            
            if updates:
                updates.append('updated_at = CURRENT_TIMESTAMP')
                params.append(contact_id)
                
                query = f"UPDATE contacts SET {', '.join(updates)} WHERE id = ?"
                cursor.execute(query, params)
            
            # Groups yangilash
            if 'group_ids' in kwargs:
                cursor.execute('DELETE FROM contact_group_memberships WHERE contact_id = ?', (contact_id,))
                
                for group_id in kwargs['group_ids']:
                    cursor.execute('''
                    INSERT INTO contact_group_memberships (contact_id, group_id) 
                    VALUES (?, ?)
                    ''', (contact_id, group_id))
            
            # Tags yangilash
            if 'tag_ids' in kwargs:
                cursor.execute('DELETE FROM contact_tag_assignments WHERE contact_id = ?', (contact_id,))
                
                for tag_id in kwargs['tag_ids']:
                    cursor.execute('''
                    INSERT INTO contact_tag_assignments (contact_id, tag_id) 
                    VALUES (?, ?)
                    ''', (contact_id, tag_id))
            
            conn.commit()
            return cursor.rowcount > 0
    
    def delete_contact(self, contact_id):
        """Contact o'chirish"""
        with sqlite3.connect(self.db_path) as conn:
            cursor = conn.cursor()
            cursor.execute('DELETE FROM contacts WHERE id = ?', (contact_id,))
            conn.commit()
            return cursor.rowcount > 0
    
    def add_interaction(self, contact_id, interaction_type, subject=None, notes=None):
        """Contact bilan aloqa qo'shish"""
        with sqlite3.connect(self.db_path) as conn:
            cursor = conn.cursor()
            cursor.execute('''
            INSERT INTO contact_interactions (contact_id, interaction_type, subject, notes) 
            VALUES (?, ?, ?, ?)
            ''', (contact_id, interaction_type, subject, notes))
            conn.commit()
            return cursor.lastrowid
    
    def get_groups(self):
        """Barcha groups olish"""
        with sqlite3.connect(self.db_path) as conn:
            cursor = conn.cursor()
            cursor.execute('SELECT * FROM contact_groups ORDER BY name')
            return cursor.fetchall()
    
    def get_tags(self):
        """Barcha tags olish"""
        with sqlite3.connect(self.db_path) as conn:
            cursor = conn.cursor()
            cursor.execute('SELECT * FROM contact_tags ORDER BY name')
            return cursor.fetchall()
    
    def get_contact_statistics(self):
        """Contact statistikasi"""
        with sqlite3.connect(self.db_path) as conn:
            cursor = conn.cursor()
            
            # Umumiy statistika
            cursor.execute('''
            SELECT 
                COUNT(*) as total_contacts,
                SUM(CASE WHEN is_favorite = 1 THEN 1 ELSE 0 END) as favorite_contacts,
                COUNT(DISTINCT company) as unique_companies
            FROM contacts
            ''')
            
            stats = cursor.fetchone()
            
            # Groups bo'yicha statistika
            cursor.execute('''
            SELECT cg.name, COUNT(cgm.contact_id) as count
            FROM contact_groups cg
            LEFT JOIN contact_group_memberships cgm ON cg.id = cgm.group_id
            GROUP BY cg.id, cg.name
            ORDER BY count DESC
            ''')
            
            group_stats = cursor.fetchall()
            
            # Tags bo'yicha statistika
            cursor.execute('''
            SELECT ct.name, COUNT(cta.contact_id) as count
            FROM contact_tags ct
            LEFT JOIN contact_tag_assignments cta ON ct.id = cta.tag_id
            GROUP BY ct.id, ct.name
            ORDER BY count DESC
            ''')
            
            tag_stats = cursor.fetchall()
            
            return {
                'total': stats[0],
                'favorites': stats[1],
                'companies': stats[2],
                'group_stats': group_stats,
                'tag_stats': tag_stats
            }

3. Flask App

# app.py
from flask import Flask, render_template, request, redirect, url_for, flash, jsonify
from database import ContactsDatabase
from datetime import datetime

app = Flask(__name__)
app.secret_key = 'your-secret-key-here'

# Database instance
db = ContactsDatabase()

@app.route('/')
def index():
    """Asosiy sahifa"""
    # Filter parametrlari
    search = request.args.get('search')
    group_id = request.args.get('group_id')
    tag_id = request.args.get('tag_id')
    is_favorite = request.args.get('is_favorite')
    
    # Contactlarni olish
    contacts = db.get_all_contacts(
        search=search, 
        group_id=group_id, 
        tag_id=tag_id, 
        is_favorite=is_favorite
    )
    
    # Groups va tags olish
    groups = db.get_groups()
    tags = db.get_tags()
    
    # Statistika
    stats = db.get_contact_statistics()
    
    return render_template('index.html', 
                         contacts=contacts, 
                         groups=groups, 
                         tags=tags,
                         stats=stats,
                         current_filters={
                             'search': search,
                             'group_id': group_id,
                             'tag_id': tag_id,
                             'is_favorite': is_favorite
                         })

@app.route('/contact/')
def contact_detail(contact_id):
    """Contact tafsilotlari"""
    contact_data = db.get_contact_by_id(contact_id)
    
    if not contact_data:
        flash('Contact topilmadi!', 'error')
        return redirect(url_for('index'))
    
    return render_template('contact_detail.html', **contact_data)

@app.route('/add', methods=['GET', 'POST'])
def add_contact():
    """Yangi contact qo'shish"""
    if request.method == 'POST':
        first_name = request.form['first_name']
        last_name = request.form['last_name']
        email = request.form.get('email')
        phone = request.form.get('phone')
        company = request.form.get('company')
        job_title = request.form.get('job_title')
        notes = request.form.get('notes')
        avatar_url = request.form.get('avatar_url')
        is_favorite = 'is_favorite' in request.form
        
        group_ids = request.form.getlist('groups')
        tag_ids = request.form.getlist('tags')
        
        # Group va tag IDs ni integer ga o'tkazish
        group_ids = [int(gid) for gid in group_ids if gid]
        tag_ids = [int(tid) for tid in tag_ids if tid]
        
        # Contact yaratish
        contact_id = db.create_contact(
            first_name=first_name,
            last_name=last_name,
            email=email,
            phone=phone,
            company=company,
            job_title=job_title,
            notes=notes,
            avatar_url=avatar_url,
            group_ids=group_ids,
            tag_ids=tag_ids
        )
        
        flash('Contact muvaffaqiyatli yaratildi!', 'success')
        return redirect(url_for('contact_detail', contact_id=contact_id))
    
    # Groups va tags olish
    groups = db.get_groups()
    tags = db.get_tags()
    
    return render_template('add_contact.html', groups=groups, tags=tags)

@app.route('/edit/', methods=['GET', 'POST'])
def edit_contact(contact_id):
    """Contact tahrirlash"""
    if request.method == 'POST':
        first_name = request.form['first_name']
        last_name = request.form['last_name']
        email = request.form.get('email')
        phone = request.form.get('phone')
        company = request.form.get('company')
        job_title = request.form.get('job_title')
        notes = request.form.get('notes')
        avatar_url = request.form.get('avatar_url')
        is_favorite = 'is_favorite' in request.form
        
        group_ids = request.form.getlist('groups')
        tag_ids = request.form.getlist('tags')
        
        # Group va tag IDs ni integer ga o'tkazish
        group_ids = [int(gid) for gid in group_ids if gid]
        tag_ids = [int(tid) for tid in tag_ids if tid]
        
        # Contact yangilash
        success = db.update_contact(
            contact_id=contact_id,
            first_name=first_name,
            last_name=last_name,
            email=email,
            phone=phone,
            company=company,
            job_title=job_title,
            notes=notes,
            avatar_url=avatar_url,
            is_favorite=is_favorite,
            group_ids=group_ids,
            tag_ids=tag_ids
        )
        
        if success:
            flash('Contact muvaffaqiyatli yangilandi!', 'success')
        else:
            flash('Contact yangilanmadi!', 'error')
        
        return redirect(url_for('contact_detail', contact_id=contact_id))
    
    # Contact ma'lumotlarini olish
    contact_data = db.get_contact_by_id(contact_id)
    if not contact_data:
        flash('Contact topilmadi!', 'error')
        return redirect(url_for('index'))
    
    # Groups va tags olish
    groups = db.get_groups()
    tags = db.get_tags()
    
    return render_template('edit_contact.html', 
                         contact_data=contact_data,
                         groups=groups, 
                         tags=tags)

@app.route('/delete/')
def delete_contact(contact_id):
    """Contact o'chirish"""
    success = db.delete_contact(contact_id)
    
    if success:
        flash('Contact muvaffaqiyatli o''chirildi!', 'success')
    else:
        flash('Contact o''chirilmadi!', 'error')
    
    return redirect(url_for('index'))

@app.route('/favorite/')
def toggle_favorite(contact_id):
    """Favorite holatini o'zgartirish"""
    contact_data = db.get_contact_by_id(contact_id)
    
    if contact_data:
        current_favorite = contact_data['contact'][7]  # is_favorite ustuni
        new_favorite = not current_favorite
        
        success = db.update_contact(contact_id, is_favorite=new_favorite)
        
        if success:
            flash('Favorite holati o''zgartirildi!', 'success')
        else:
            flash('Favorite holati o''zgartirilmadi!', 'error')
    
    return redirect(url_for('contact_detail', contact_id=contact_id))

@app.route('/add_interaction/', methods=['POST'])
def add_interaction(contact_id):
    """Contact bilan aloqa qo'shish"""
    interaction_type = request.form['interaction_type']
    subject = request.form.get('subject')
    notes = request.form.get('notes')
    
    interaction_id = db.add_interaction(
        contact_id=contact_id,
        interaction_type=interaction_type,
        subject=subject,
        notes=notes
    )
    
    if interaction_id:
        flash('Aloqa muvaffaqiyatli qo''shildi!', 'success')
    else:
        flash('Aloqa qo''shilmadi!', 'error')
    
    return redirect(url_for('contact_detail', contact_id=contact_id))

@app.route('/api/contacts')
def api_contacts():
    """API - Contactlar"""
    search = request.args.get('search')
    group_id = request.args.get('group_id')
    tag_id = request.args.get('tag_id')
    is_favorite = request.args.get('is_favorite')
    
    contacts = db.get_all_contacts(
        search=search, 
        group_id=group_id, 
        tag_id=tag_id, 
        is_favorite=is_favorite
    )
    
    # JSON formatga o'tkazish
    contacts_data = []
    for contact in contacts:
        contacts_data.append({
            'id': contact[0],
            'first_name': contact[1],
            'last_name': contact[2],
            'email': contact[3],
            'phone': contact[4],
            'company': contact[5],
            'job_title': contact[6],
            'is_favorite': bool(contact[7]),
            'created_at': contact[8],
            'updated_at': contact[9],
            'groups': contact[10] if contact[10] else '',
            'tags': contact[11] if contact[11] else ''
        })
    
    return jsonify(contacts_data)

@app.route('/api/statistics')
def api_statistics():
    """API - Statistika"""
    stats = db.get_contact_statistics()
    return jsonify(stats)

if __name__ == '__main__':
    app.run(debug=True)

4. Loyiha xususiyatlari