读取License文件,并存入sqlite3数据库。

#!/Users/wanlinwang/spack/opt/spack/darwin-ventura-m1/apple-clang-14.0.3/python-3.10.8-b7rgkczw4yfgrkbgttbcbur3ksmwho5d/bin/python3


import sqlite3
import re
import argparse

def parse_license_file(filename):
    with open(filename, 'r') as f:
        content = f.read()
        start_index = content.find("##     PRODUCT TO FEATURE MAPPING")
        products_data = content[start_index:].split("# Product Id  :")

    products = []
    for product_data in products_data[1:]:
        product = {}
        product['id'] = product_data.split(",")[0].strip()
        product['name'] = re.search(r'Product Name: (.+)', product_data).group(1).strip()
        product['version'] = re.search(r'\[Version: (.+?)\]', product_data).group(1).strip()
        product['features'] = re.findall(r'Feature: (.+?)\s*\[', product_data)
        product['dates'] = re.findall(r'Start Date: (.+?) Exp Date: (.+?)\s*Product Qty: (\d+)', product_data)
        products.append(product)
    return products

def store_to_db(products, filename):
    conn = sqlite3.connect('license.db')
    cursor = conn.cursor()

    # Create tables
    cursor.execute('''CREATE TABLE IF NOT EXISTS LicenseFiles
                     (LicenseFileId INTEGER PRIMARY KEY AUTOINCREMENT, FileName TEXT UNIQUE, hostname TEXT, hostid TEXT, lmgrd_port TEXT, vendor_daemon_port TEXT, lmgrd_file_id INTEGER, vendor_daemon_file_id INTEGER, options_file_id INTEGER)''')
    cursor.execute("INSERT OR IGNORE INTO LicenseFiles (FileName) VALUES (?)", (filename,))
    cursor.execute("SELECT LicenseFileId FROM LicenseFiles WHERE FileName=?", (filename,))
    license_file_id = cursor.fetchone()[0]

    cursor.execute('''CREATE TABLE IF NOT EXISTS LicenseRelatedFiles
                     (ExecutableFileId INTEGER PRIMARY KEY AUTOINCREMENT, FileName TEXT UNIQUE)''')

    cursor.execute('''CREATE TABLE IF NOT EXISTS Features
                     (FeatureId INTEGER PRIMARY KEY AUTOINCREMENT, FeatureName TEXT UNIQUE)''')

    cursor.execute('''CREATE TABLE IF NOT EXISTS Products
                     (ProductId TEXT PRIMARY KEY, ProductName TEXT)''')

    cursor.execute('''CREATE TABLE IF NOT EXISTS ProductFeatureRelation
                     (RelationId INTEGER PRIMARY KEY AUTOINCREMENT, ProductId TEXT, FeatureId INTEGER, 
                     FOREIGN KEY(ProductId) REFERENCES Products(ProductId), 
                     FOREIGN KEY(FeatureId) REFERENCES Features(FeatureId))''')

    cursor.execute('''CREATE TABLE IF NOT EXISTS ProductDates
                     (DateId INTEGER PRIMARY KEY AUTOINCREMENT, ProductId TEXT, StartDate DATE, EndDate DATE, Version TEXT, Quantity INTEGER, LicenseFileId INTEGER,
                     FOREIGN KEY(ProductId) REFERENCES Products(ProductId),
                     FOREIGN KEY(LicenseFileId) REFERENCES LicenseFiles(LicenseFileId))''')

    for product in products:
        cursor.execute("INSERT OR REPLACE INTO Products (ProductId, ProductName) VALUES (?, ?)", (product['id'], product['name']))

        for feature in product['features']:
            cursor.execute("INSERT OR IGNORE INTO Features (FeatureName) VALUES (?)", (feature,))
            cursor.execute("SELECT FeatureId FROM Features WHERE FeatureName=?", (feature,))
            feature_id = cursor.fetchone()[0]
            
            cursor.execute("SELECT 1 FROM ProductFeatureRelation WHERE ProductId=? AND FeatureId=?", (product['id'], feature_id))
            if not cursor.fetchone():
                cursor.execute("INSERT INTO ProductFeatureRelation (ProductId, FeatureId) VALUES (?, ?)", (product['id'], feature_id))
        
        for start_date, end_date, quantity in product['dates']:
            cursor.execute("SELECT 1 FROM ProductDates WHERE ProductId=? AND StartDate=? AND EndDate=? AND Version=? AND LicenseFileId=?", (product['id'], start_date, end_date, product['version'], license_file_id))
            if not cursor.fetchone():
                cursor.execute("INSERT INTO ProductDates (ProductId, StartDate, EndDate, Version, Quantity, LicenseFileId) VALUES (?, ?, ?, ?, ?, ?)", (product['id'], start_date, end_date, product['version'], quantity, license_file_id))

    conn.commit()
    conn.close()

def main():
    parser = argparse.ArgumentParser(description="Process License Files and Store to Database")
    parser.add_argument('license_files', metavar='N', type=str, nargs='+', help='License file(s) to process')
    
    args = parser.parse_args()

    for filename in args.license_files:
        products = parse_license_file(filename)
        store_to_db(products, filename)

if __name__ == "__main__":
    main()

读取sqlite3数据库,并显示在客户端内,

#!/home/centos/spack/opt/spack/linux-centos7-x86_64_v4/gcc-13.2.0/python-3.10.10-64a4t27o3c2cnk23lenooydikdsiqzgv/bin/python3


import sqlite3
import tkinter as tk
from tkinter import ttk
from contextlib import contextmanager
import tkinter.messagebox
from datetime import datetime, timedelta
from collections import defaultdict
from tkinter import filedialog
import openpyxl

def get_row_color(start_date, end_date):
    current_date = datetime.now().date()
    end_date_obj = datetime.strptime(end_date, "%d-%b-%Y").date()

    if end_date_obj < current_date:
        return "light gray"
    elif current_date <= end_date_obj <= (current_date + timedelta(days=14)):
        return "red"
    else:
        return "black"

@contextmanager
def get_db_connection():
    conn = sqlite3.connect('license.db')
    try:
        yield conn
    finally:
        conn.close()

def execute_query(query, params=()):
    with get_db_connection() as conn:
        cursor = conn.cursor()
        cursor.execute(query, params)
        return cursor.fetchall()

def execute_commit(query, params=()):
    with get_db_connection() as conn:
        cursor = conn.cursor()
        cursor.execute(query, params)
        conn.commit()

def retrieve_license_info_by_feature(feature_name):
    query = """
    SELECT pd.StartDate, pd.EndDate, pd.Quantity, lf.FileName, p.ProductId
    FROM ProductDates pd
    JOIN Products p ON pd.ProductId = p.ProductId
    JOIN ProductFeatureRelation pfr ON p.ProductId = pfr.ProductId
    JOIN Features f ON pfr.FeatureId = f.FeatureId
    JOIN LicenseFiles lf ON pd.LicenseFileId = lf.LicenseFileId
    WHERE f.FeatureName = ?
    """
    return execute_query(query, (feature_name,))

def retrieve_license_info_by_product(product_id):
    query = """
    SELECT pd.StartDate, pd.EndDate, pd.Quantity, lf.FileName, p.ProductId
    FROM ProductDates pd
    JOIN Products p ON pd.ProductId = p.ProductId
    JOIN LicenseFiles lf ON pd.LicenseFileId = lf.LicenseFileId
    WHERE p.ProductId = ?
    """
    return execute_query(query, (product_id,))

def get_all_features():
    return [row[0] for row in execute_query("SELECT FeatureName FROM Features")]

def get_all_products():
    return [row[0] for row in execute_query("SELECT ProductId FROM Products")]

def get_all_license_files():
    return [row[0] for row in execute_query("SELECT FileName FROM LicenseFiles")]

def get_all_license_related_files():
    return [row[0] for row in execute_query("SELECT FileName FROM LicenseRelatedFiles")]

def on_search_by_feature():
    tree_by_feature.delete(*tree_by_feature.get_children())  # Clear the treeview
    feature_name = feature_name_combobox.get()
    results = retrieve_license_info_by_feature(feature_name)
    sorted_results = sorted(results, key=lambda x: datetime.strptime(x[1], "%d-%b-%Y"))
    for result in sorted_results:
        color = get_row_color(result[0], result[1])
        tree_by_feature.insert("", "end", values=result, tags=(color,))
        tree_by_feature.tag_configure(color, foreground=color)


def on_search_by_product():
    tree_by_product.delete(*tree_by_product.get_children())  # Clear the treeview
    product_id = product_id_combobox.get()
    results = retrieve_license_info_by_product(product_id)
    sorted_results = sorted(results, key=lambda x: datetime.strptime(x[1], "%d-%b-%Y"))
    for result in sorted_results:
        color = get_row_color(result[0], result[1])
        tree_by_product.insert("", "end", values=result, tags=(color,))
        tree_by_product.tag_configure(color, foreground=color)

def load_license_info(event):
    data = execute_query("SELECT hostname, hostid, lmgrd_port, vendor_daemon_port, lmgrd_file_id, vendor_daemon_file_id, options_file_id FROM LicenseFiles WHERE FileName=?", (license_file_combobox.get(),))
    if data:
        data = data[0]
        
        hostname_entry.delete(0, tk.END)
        hostid_entry.delete(0, tk.END)
        lmgrd_port_entry.delete(0, tk.END)
        vendor_daemon_port_entry.delete(0, tk.END)

        # Clear all entries and comboboxes first
        hostname_entry.delete(0, tk.END)
        hostid_entry.delete(0, tk.END)
        lmgrd_port_entry.delete(0, tk.END)
        vendor_daemon_port_entry.delete(0, tk.END)
        lmgrd_file_combobox.set('')
        vendor_daemon_file_combobox.set('')
        options_file_combobox.set('')

        if data:
            if data[0]:
                hostname_entry.insert(0, data[0])
            if data[1]:
                hostid_entry.insert(0, data[1])
            if data[2]:
                lmgrd_port_entry.insert(0, data[2])
            if data[3]:
                vendor_daemon_port_entry.insert(0, data[3])
            if data[4]:
                lmgrd_file = execute_query("SELECT FileName FROM LicenseRelatedFiles WHERE ExecutableFileId=?", (data[4],))
                if lmgrd_file:
                    lmgrd_file = lmgrd_file[0]
                    lmgrd_file_combobox.set(lmgrd_file)
            if data[5]:
                vendor_daemon_file = execute_query("SELECT FileName FROM LicenseRelatedFiles WHERE ExecutableFileId=?", (data[5],))
                if vendor_daemon_file:
                    vendor_daemon_file = vendor_daemon_file[0]
                    vendor_daemon_file_combobox.set(vendor_daemon_file)
            if data[6]:
                options_file = execute_query("SELECT FileName FROM LicenseRelatedFiles WHERE ExecutableFileId=?", (data[6],))
                if options_file:
                    options_file = options_file[0]
                    options_file_combobox.set(options_file)

        if (not hostname_entry.get() and not lmgrd_port_entry.get() and not vendor_daemon_port_entry.get() and not lmgrd_file_combobox.get() and not vendor_daemon_file_combobox.get() and not options_file_combobox.get()):
            copy_from_prev_button['state'] = tk.NORMAL
        else:
            copy_from_prev_button['state'] = tk.DISABLED

def check_inputs_filled():
    # Check if all required fields are filled
    if (license_file_combobox.get() and hostname_entry.get() and hostid_entry.get() and lmgrd_port_entry.get() and lmgrd_file_combobox.get() and vendor_daemon_file_combobox.get()):
        save_button['state'] = tk.NORMAL
        modify_button['state'] = tk.NORMAL
    else:
        save_button['state'] = tk.DISABLED
        modify_button['state'] = tk.DISABLED

def copy_from_previous_file():
    license_file_name = license_file_combobox.get()
    if not license_file_name:
        tkinter.messagebox.showerror("Error", "Please select a License File first.")
        return

    # Extract hostid from the license file's filename
    try:
        hostid = license_file_name.split('_')[2]
    except IndexError:
        tkinter.messagebox.showerror("Error", "Invalid License File name format.")
        return

    data = execute_query("SELECT hostname, lmgrd_port, vendor_daemon_port, lmgrd_file_id, vendor_daemon_file_id, options_file_id FROM LicenseFiles WHERE hostid=? ORDER BY LicenseFileId DESC LIMIT 1", (hostid,))
    if data:
        data = data[0]
    else:
        tkinter.messagebox.showinfo("Info", "No previous data found for the given Host ID.")
        return

    hostname, lmgrd_port, vendor_daemon_port, lmgrd_file_id, vendor_daemon_file_id, options_file_id = data

    # Populate the fields
    hostname_entry.delete(0, tk.END)
    hostname_entry.insert(0, hostname)

    hostid_entry.delete(0, tk.END)
    hostid_entry.insert(0, hostid)

    lmgrd_port_entry.delete(0, tk.END)
    lmgrd_port_entry.insert(0, lmgrd_port)

    vendor_daemon_port_entry.delete(0, tk.END)
    vendor_daemon_port_entry.insert(0, vendor_daemon_port)

    # Fetch filenames for the file IDs
    lmgrd_file = execute_query("SELECT FileName FROM LicenseRelatedFiles WHERE ExecutableFileId=?", (lmgrd_file_id,))
    if lmgrd_file:
        lmgrd_file = lmgrd_file[0]
        lmgrd_file_combobox.set(lmgrd_file)
    vendor_daemon_file = execute_query("SELECT FileName FROM LicenseRelatedFiles WHERE ExecutableFileId=?", (vendor_daemon_file_id,))
    if vendor_daemon_file:
        vendor_daemon_file = vendor_daemon_file[0]
        vendor_daemon_file_combobox.set(vendor_daemon_file)

    if options_file_id:
        options_file = execute_query("SELECT FileName FROM LicenseRelatedFiles WHERE ExecutableFileId=?", (options_file_id,))
        if options_file:
            options_file = options_file[0]
            options_file_combobox.set(options_file)

    # Enable the "Copy From Previous File" button only when the other fields are empty
    if not hostname_entry.get() and not lmgrd_port_entry.get() and not vendor_daemon_port_entry.get():
        copy_from_prev_button['state'] = tk.NORMAL
    else:
        copy_from_prev_button['state'] = tk.DISABLED

def save_license_info():
    license_file_id = execute_query("SELECT LicenseFileId FROM LicenseFiles WHERE FileName=?", (license_file_combobox.get(),))
    if license_file_id:
        license_file_id = license_file_id[0]
        
        lmgrd_file = lmgrd_file_combobox.get()
        vendor_daemon_file = vendor_daemon_file_combobox.get()
        options_file = options_file_combobox.get()

        update_sub_str_list = []
        insert_sub_str_list = []
        value_sub_str_list = []
        if lmgrd_file:
            execute_commit("INSERT OR IGNORE INTO LicenseRelatedFiles (FileName) VALUES (?)", (lmgrd_file,))
            lmgrd_file_id = execute_query("SELECT ExecutableFileId FROM LicenseRelatedFiles WHERE FileName=?", (lmgrd_file,))
            if lmgrd_file_id:
                lmgrd_file_id = lmgrd_file_id[0][0]
                update_sub_str_list.append("lmgrd_file_id=?")
                insert_sub_str_list.append("lmgrd_file_id")
                value_sub_str_list.append(lmgrd_file_id)

        if vendor_daemon_file:
            execute_commit("INSERT OR IGNORE INTO LicenseRelatedFiles (FileName) VALUES (?)", (vendor_daemon_file,))
            vendor_daemon_file_id = execute_query("SELECT ExecutableFileId FROM LicenseRelatedFiles WHERE FileName=?", (vendor_daemon_file,))
            if vendor_daemon_file_id:
                vendor_daemon_file_id = vendor_daemon_file_id[0][0]
                update_sub_str_list.append("vendor_daemon_file_id=?")
                insert_sub_str_list.append("vendor_daemon_file_id")
                value_sub_str_list.append(vendor_daemon_file_id)

        if options_file:
            # Check if options_file exists.
            execute_commit("INSERT OR IGNORE INTO LicenseRelatedFiles (FileName) VALUES (?)", (options_file,))
            options_file_id = execute_query("SELECT ExecutableFileId FROM LicenseRelatedFiles WHERE FileName=?", (options_file,))
            if options_file_id:
                options_file_id = options_file_id[0][0]
                update_sub_str_list.append("options_file_id=?")
                insert_sub_str_list.append("options_file_id")
                value_sub_str_list.append(options_file_id)

        if license_file_id:
            # Update the existing record
            #cursor.execute("UPDATE LicenseFiles SET hostname=?, hostid=?, lmgrd_port=?, vendor_daemon_port=?, lmgrd_file_id=?, vendor_daemon_file_id=?, options_file_id=? WHERE LicenseFileId=?", 
            #               (hostname_entry.get(), hostid_entry.get(), lmgrd_port_entry.get(), vendor_daemon_port_entry.get(), lmgrd_file_id, vendor_daemon_file_id, options_file_id, license_file_id[0]))
            if lmgrd_file or vendor_daemon_file or options_file:
                param = (
                    hostname_entry.get(), 
                    hostid_entry.get(), 
                    lmgrd_port_entry.get(), 
                    vendor_daemon_port_entry.get(), 
                    *value_sub_str_list, 
                    license_file_id[0]
                )
                execute_commit(f"UPDATE LicenseFiles SET hostname=?, hostid=?, lmgrd_port=?, vendor_daemon_port=?, {','.join(update_sub_str_list)} WHERE LicenseFileId=?", param)
            else:
                execute_commit("UPDATE LicenseFiles SET hostname=?, hostid=?, lmgrd_port=?, vendor_daemon_port=? WHERE LicenseFileId=?", 
                               (hostname_entry.get(), hostid_entry.get(), lmgrd_port_entry.get(), vendor_daemon_port_entry.get(), license_file_id[0]))
        else:
            # Insert a new record
            #cursor.execute("INSERT INTO LicenseFiles (FileName, hostname, hostid, lmgrd_port, vendor_daemon_port, lmgrd_file_id, vendor_daemon_file_id, options_file_id) VALUES (?, ?, ?, ?, ?, ?, ?, ?)", 
            #               (license_file_combobox.get(), hostname_entry.get(), hostid_entry.get(), lmgrd_port_entry.get(), vendor_daemon_port_entry.get(), lmgrd_file_id, vendor_daemon_file_id, options_file_id))
            if lmgrd_file or vendor_daemon_file or options_file:
                param = (
                    license_file_combobox.get(),
                    hostname_entry.get(), 
                    hostid_entry.get(), 
                    lmgrd_port_entry.get(), 
                    vendor_daemon_port_entry.get(), 
                    *value_sub_str_list
                )
                execute_commit(f"INSERT INTO LicenseFiles (FileName, hostname, hostid, lmgrd_port, vendor_daemon_port, {','.join(insert_sub_str_list)}) VALUES (?, ?, ?, ?, ?, {','.join(['?'] * len(insert_sub_str_list))})", param)
            else:
                execute_commit("INSERT INTO LicenseFiles (FileName, hostname, hostid, lmgrd_port, vendor_daemon_port, lmgrd_file_id, vendor_daemon_file_id, options_file_id) VALUES (?, ?, ?, ?, ?)", 
                               (license_file_combobox.get(), hostname_entry.get(), hostid_entry.get(), lmgrd_port_entry.get(), vendor_daemon_port_entry.get()))
        
        #conn.commit()
        # Disable "Save" button after saved.
        save_button['state'] = tk.DISABLED
        tk.messagebox.showinfo("Info", "License file config saved successfully!")

app = tk.Tk()
app.title("License Manager")

notebook = ttk.Notebook(app)
notebook.pack(pady=10, padx=10, expand=True, fill='both')

# Tab for Feature Name
tab_feature = ttk.Frame(notebook)
notebook.add(tab_feature, text="By Feature Name")

feature_names = get_all_features()
ttk.Label(tab_feature, text="Feature Name:").grid(row=0, column=0, sticky=tk.W, pady=5, padx=5)
feature_name_combobox = ttk.Combobox(tab_feature, values=feature_names, width=30)
feature_name_combobox.grid(row=0, column=1, pady=5, padx=5)
ttk.Button(tab_feature, text="Search", command=on_search_by_feature).grid(row=0, column=2, pady=5, padx=5)

tree_by_feature = ttk.Treeview(tab_feature, columns=("StartDate", "EndDate", "Quantity", "FileName", "ProductId"), show="headings")
tree_by_feature.heading("StartDate", text="Start Date")
tree_by_feature.heading("EndDate", text="End Date")
tree_by_feature.heading("Quantity", text="Quantity")
tree_by_feature.heading("FileName", text="License File")
tree_by_feature.heading("ProductId", text="Product Id")
tree_by_feature.grid(row=1, column=0, columnspan=3, pady=10, padx=5, sticky=(tk.W, tk.E))

# Tab for Product Id
tab_product = ttk.Frame(notebook)
notebook.add(tab_product, text="By Product Id")

product_ids = get_all_products()
ttk.Label(tab_product, text="Product Id:").grid(row=0, column=0, sticky=tk.W, pady=5, padx=5)
product_id_combobox = ttk.Combobox(tab_product, values=product_ids, width=30)
product_id_combobox.grid(row=0, column=1, pady=5, padx=5)
ttk.Button(tab_product, text="Search", command=on_search_by_product).grid(row=0, column=2, pady=5, padx=5)

tree_by_product = ttk.Treeview(tab_product, columns=("StartDate", "EndDate", "Quantity", "FileName", "ProductId"), show="headings")
tree_by_product.heading("StartDate", text="Start Date")
tree_by_product.heading("EndDate", text="End Date")
tree_by_product.heading("Quantity", text="Quantity")
tree_by_product.heading("FileName", text="License File")
tree_by_product.heading("ProductId", text="Product Id")
tree_by_product.grid(row=1, column=0, columnspan=3, pady=10, padx=5, sticky=(tk.W, tk.E))

# Tab for License Settings
tab_settings = ttk.Frame(notebook)
notebook.add(tab_settings, text="License Settings")

license_files = get_all_license_files()
ttk.Label(tab_settings, text="License File:").grid(row=0, column=0, sticky=tk.W, pady=5, padx=5)
license_file_combobox = ttk.Combobox(tab_settings, values=license_files, width=64)
license_file_combobox.grid(row=0, column=1, pady=5, padx=5)
license_file_combobox.bind("<<ComboboxSelected>>", load_license_info)

ttk.Label(tab_settings, text="Hostname:").grid(row=1, column=0, sticky=tk.W, pady=5, padx=5)
hostname_entry = ttk.Entry(tab_settings, width=64)
hostname_entry.grid(row=1, column=1, pady=5, padx=5)

ttk.Label(tab_settings, text="Host ID:").grid(row=2, column=0, sticky=tk.W, pady=5, padx=5)
hostid_entry = ttk.Entry(tab_settings, width=64)
hostid_entry.grid(row=2, column=1, pady=5, padx=5)

ttk.Label(tab_settings, text="Lmgrd Port:").grid(row=3, column=0, sticky=tk.W, pady=5, padx=5)
lmgrd_port_entry = ttk.Entry(tab_settings, width=64)
lmgrd_port_entry.grid(row=3, column=1, pady=5, padx=5)

ttk.Label(tab_settings, text="Vendor Daemon Port:").grid(row=4, column=0, sticky=tk.W, pady=5, padx=5)
vendor_daemon_port_entry = ttk.Entry(tab_settings, width=64)
vendor_daemon_port_entry.grid(row=4, column=1, pady=5, padx=5)

license_related_files = get_all_license_related_files()
ttk.Label(tab_settings, text="LMGRD FILE:").grid(row=5, column=0, sticky=tk.W, pady=5, padx=5)
lmgrd_file_combobox = ttk.Combobox(tab_settings, values=license_related_files, width=64)
lmgrd_file_combobox.grid(row=5, column=1, pady=5, padx=5)

ttk.Label(tab_settings, text="VENDOR DAEMON FILE:").grid(row=6, column=0, sticky=tk.W, pady=5, padx=5)
vendor_daemon_file_combobox = ttk.Combobox(tab_settings, values=license_related_files, width=64)
vendor_daemon_file_combobox.grid(row=6, column=1, pady=5, padx=5)

ttk.Label(tab_settings, text="OPTIONS FILE:").grid(row=7, column=0, sticky=tk.W, pady=5, padx=5)
options_file_combobox = ttk.Combobox(tab_settings, values=license_related_files, width=64)
options_file_combobox.grid(row=7, column=1, pady=5, padx=5)

# Bind the check_inputs_filled function to the inputs
hostname_entry.bind("<KeyRelease>", lambda e: check_inputs_filled())
hostid_entry.bind("<KeyRelease>", lambda e: check_inputs_filled())
lmgrd_port_entry.bind("<KeyRelease>", lambda e: check_inputs_filled())
vendor_daemon_port_entry.bind("<KeyRelease>", lambda e: check_inputs_filled())
lmgrd_file_combobox.bind("<<ComboboxSelected>>", lambda e: check_inputs_filled())
vendor_daemon_file_combobox.bind("<<ComboboxSelected>>", lambda e: check_inputs_filled())
options_file_combobox.bind("<<ComboboxSelected>>", lambda e: check_inputs_filled())

save_button = ttk.Button(tab_settings, text="Save to DB", command=save_license_info, state=tk.DISABLED)
save_button.grid(row=8, column=1, pady=10, padx=5, sticky=tk.E)

copy_from_prev_button = ttk.Button(tab_settings, text="Copy From Previous File", command=copy_from_previous_file, state=tk.DISABLED)
copy_from_prev_button.grid(row=9, column=1, pady=10, padx=5, sticky=tk.W)

def modify_license_file():
    license_file = license_file_combobox.get()
    hostname = hostname_entry.get()
    hostid = hostid_entry.get()
    lmgrd_port = lmgrd_port_entry.get()
    vendor_daemon_port = vendor_daemon_port_entry.get()
    vendor_daemon_file = vendor_daemon_file_combobox.get()
    options_file = options_file_combobox.get()

    with open(license_file, 'r') as f:
        lines = f.readlines()

    modified = False
    for i, line in enumerate(lines):
        if line.startswith("SERVER"):
            if line != f"SERVER {hostname} {hostid} {lmgrd_port}\n":
                lines[i] = f"SERVER {hostname} {hostid} {lmgrd_port}\n"
                modified = True
        elif line.startswith("DAEMON"):
            daemon_line = f"DAEMON cdslmd {vendor_daemon_file}"
            if options_file:
                daemon_line = daemon_line + f" OPTIONS={options_file}"
            if vendor_daemon_port:
                daemon_line = daemon_line + f" PORT={vendor_daemon_port}"
            if options_file and line != f"{daemon_line}\n":
                lines[i] = f"{daemon_line}\n"
                modified = True

    if modified:
        with open(license_file, 'w') as f:
            f.writelines(lines)
        tk.messagebox.showinfo("Info", "License file modified successfully!")
    else:
        tk.messagebox.showinfo("Info", "License file is already up-to-date!")

    # Disable modify_button
    modify_button['state'] = tk.DISABLED

# 在License Settings tab中添加Modify按钮
modify_button = ttk.Button(tab_settings, text="Modify Lic File", command=modify_license_file, state=tk.DISABLED)
modify_button.grid(row=9, column=1, pady=10, padx=5, sticky=tk.E)

# 比较两个License文件差异
def compare_license_files():
    file1 = license_file_combobox1.get()
    file2 = license_file_combobox2.get()

    if not file1 or not file2:
        tk.messagebox.showerror("Error", "Please select both license files.")
        return

    data1 = retrieve_license_info_by_file(file1)
    data2 = retrieve_license_info_by_file(file2)

    # Convert data to a dictionary with product_id as key and list of date ranges as values
    dict1 = defaultdict(list)
    dict2 = defaultdict(list)

    for item in data1:
        dict1[item[4]].append(item)
    for item in data2:
        dict2[item[4]].append(item)

    compare_tree.delete(*compare_tree.get_children())  # Clear the treeview

    for product_id, date_ranges1 in dict1.items():
        if product_id in dict2:
            date_ranges2 = dict2[product_id]
            for range1 in date_ranges1:
                for range2 in date_ranges2:
                    if range1 != range2:
                        compare_tree.insert("", "end", values=(product_id, range1[2], range2[2], range1[0] + " to " + range1[1], range2[0] + " to " + range2[1]))
            del dict2[product_id]
        else:
            for range1 in date_ranges1:
                compare_tree.insert("", "end", values=(product_id, range1[2], "Not in File 2", range1[0] + " to " + range1[1], "Not in File 2"))

    for product_id, date_ranges2 in dict2.items():
        for range2 in date_ranges2:
            compare_tree.insert("", "end", values=(product_id, "Not in File 1", range2[2], "Not in File 1", range2[0] + " to " + range2[1]))

def retrieve_license_info_by_file(file_name):
    query = """
    SELECT pd.StartDate, pd.EndDate, pd.Quantity, lf.FileName, p.ProductId
    FROM ProductDates pd
    JOIN Products p ON pd.ProductId = p.ProductId
    JOIN LicenseFiles lf ON pd.LicenseFileId = lf.LicenseFileId
    WHERE lf.FileName = ?
    """
    return execute_query(query, (file_name,))

# Code for New Tab for Comparing License Files
tab_compare = ttk.Frame(notebook)
notebook.add(tab_compare, text="Compare Lic Files")

license_files = get_all_license_files()
ttk.Label(tab_compare, text="License File 1:").grid(row=0, column=0, sticky=tk.W, pady=5, padx=5)
license_file_combobox1 = ttk.Combobox(tab_compare, values=license_files, width=30)
license_file_combobox1.grid(row=0, column=1, pady=5, padx=5)

ttk.Label(tab_compare, text="License File 2:").grid(row=1, column=0, sticky=tk.W, pady=5, padx=5)
license_file_combobox2 = ttk.Combobox(tab_compare, values=license_files, width=30)
license_file_combobox2.grid(row=1, column=1, pady=5, padx=5)

ttk.Button(tab_compare, text="Compare", command=compare_license_files).grid(row=2, column=0, columnspan=2, pady=5, padx=5)

compare_tree = ttk.Treeview(tab_compare, columns=("ProductId", "Quantity1", "Quantity2", "DateRange1", "DateRange2"), show="headings")
compare_tree.heading("ProductId", text="Product Id")
compare_tree.heading("Quantity1", text="Quantity (File 1)")
compare_tree.heading("Quantity2", text="Quantity (File 2)")
compare_tree.heading("DateRange1", text="Date Range (File 1)")
compare_tree.heading("DateRange2", text="Date Range (File 2)")
compare_tree.grid(row=3, column=0, columnspan=2, pady=10, padx=5, sticky=(tk.W, tk.E))

# License Acceptance
def load_csv_file():
    filepath = filedialog.askopenfilename(filetypes=[("CSV Files", "*.csv"), ("All Files", "*.*")])
    if not filepath:
        return

    data = {}
    with open(filepath, 'r') as file:
        reader = csv.reader(file)
        next(reader)  # skip header
        for row in reader:
            product_id, quantity = row
            data[product_id] = int(quantity)

    return data

def compare_license_to_csv():
    license_file = license_file_combobox3.get()
    csv_data = load_csv_file()

    if not license_file or not csv_data:
        tk.messagebox.showerror("Error", "Please select both license file and CSV file.")
        return

    license_data = retrieve_license_info_by_file(license_file)

    # Convert license data to a dictionary
    license_dict = defaultdict(list)
    for item in license_data:
        license_dict[item[4]].append(item)

    acceptance_tree.delete(*acceptance_tree.get_children())  # Clear the treeview

    for product_id, quantity in csv_data.items():
        if product_id in license_dict:
            quantities = ", ".join([str(item[2]) for item in license_dict[product_id][-1::-1]])
            date_ranges = ", ".join([str(item[2]) + ":" + item[0] + " to " + item[1] for item in license_dict[product_id][-1::-1]])
            acceptance_tree.insert("", "end", values=(product_id, quantity, quantities, date_ranges))
            del license_dict[product_id]
        else:
            acceptance_tree.insert("", "end", values=(product_id, quantity, "Not in License File", "Not in License File"))

    for product_id, items in license_dict.items():
        quantities = ", ".join([str(item[2]) for item in items[-1::-1]])
        date_ranges = ", ".join([str(item[2]) + ":" + item[0] + " to " + item[1] for item in items[-1::-1]])
        acceptance_tree.insert("", "end", values=(product_id, "Not in CSV", quantities, date_ranges))

def export_to_excel():
    file_name = tk.filedialog.asksaveasfilename(defaultextension=".xlsx", filetypes=[("Excel files", "*.xlsx"), ("All Files", "*.*")])
    if not file_name:
        return

    wb = openpyxl.Workbook()
    ws = wb.active
    ws.title = "License Acceptance"

    # Add headers
    headers = ["Product ID", "CSV Quantity", "License Quantity", "Date Ranges"]
    for col_num, header in enumerate(headers, 1):
        col_letter = openpyxl.utils.get_column_letter(col_num)
        ws[f"{col_letter}1"] = header
        ws[f"{col_letter}1"].font = openpyxl.styles.Font(bold=True)

    # Add data from the treeview
    for row_num, row_id in enumerate(acceptance_tree.get_children(), 2):
        row_data = acceptance_tree.item(row_id)["values"]
        for col_num, cell_data in enumerate(row_data, 1):
            ws.cell(row=row_num, column=col_num, value=cell_data)

    wb.save(file_name)
    tk.messagebox.showinfo("Info", f"Data exported to {file_name} successfully!")


# Tab for License Acceptance
tab_acceptance = ttk.Frame(notebook)
notebook.add(tab_acceptance, text="License Acceptance")

license_files = get_all_license_files()
ttk.Label(tab_acceptance, text="License File:").grid(row=0, column=0, sticky=tk.W, pady=5, padx=5)
license_file_combobox3 = ttk.Combobox(tab_acceptance, values=license_files, width=64)
license_file_combobox3.grid(row=0, column=1, pady=5, padx=5)
ttk.Button(tab_acceptance, text="Compare", command=compare_license_to_csv).grid(row=0, column=2, pady=5, padx=5)

export_button = ttk.Button(tab_acceptance, text="Export to Excel", command=export_to_excel)
export_button.grid(row=3, column=1, pady=10, padx=5, sticky=tk.E)

acceptance_tree = ttk.Treeview(tab_acceptance, columns=("ProductId", "CSV QTY", "License QTY", "Date Ranges"), show="headings")
acceptance_tree.heading("ProductId", text="Product Id")
acceptance_tree.heading("CSV QTY", text="CSV QTY")
acceptance_tree.heading("License QTY", text="License QTY")
acceptance_tree.heading("Date Ranges", text="Date Ranges")
acceptance_tree.grid(row=1, column=0, columnspan=3, pady=10, padx=5, sticky=(tk.W, tk.E))

# About Tab
tab_about = ttk.Frame(notebook)
notebook.add(tab_about, text="关于")

ttk.Label(tab_about, text="作者:wanlinwang").grid(row=0, column=0, pady=10, padx=10)
ttk.Label(tab_about, text="日期:Oct-16-2023").grid(row=1, column=0, pady=10, padx=10)
ttk.Label(tab_about, text="帮助:如有疑问,请联系wanlinwang").grid(row=2, column=0, pady=10, padx=10)

app.mainloop()

image

image

image