add support Apache AGE
This commit is contained in:
+9
-1
@@ -7,4 +7,12 @@ POSTGRES_PORT = 5432
|
|||||||
NEO_ACTIVE=false
|
NEO_ACTIVE=false
|
||||||
NEO_USER=neo4j
|
NEO_USER=neo4j
|
||||||
NEO_PASS=pass
|
NEO_PASS=pass
|
||||||
NEO_HOST=neo4j://localhost:7687
|
NEO_HOST=neo4j://localhost:7687
|
||||||
|
|
||||||
|
AGE_ACTIVE=false
|
||||||
|
AGE_USER=postgres
|
||||||
|
AGE_PASS=pass
|
||||||
|
AGE_HOST=localhost
|
||||||
|
AGE_PORT=5432
|
||||||
|
AGE_DB=university
|
||||||
|
AGE_GRAPH_NAME=movie_graph
|
||||||
@@ -1,12 +1,13 @@
|
|||||||
### Пример приложения для работы с БД
|
### Пример приложения для работы с БД
|
||||||
|
|
||||||
Небольшое десктоп‑приложение на PyQt5 для работы с PostgreSQL и Neo4j.
|
Небольшое десктоп-приложение на PyQt5 для работы с PostgreSQL, Neo4j и Apache AGE.
|
||||||
|
|
||||||
### Стек
|
### Стек
|
||||||
|
|
||||||
- PyQt5 — графический интерфейс
|
- PyQt5 — графический интерфейс
|
||||||
- PostgreSQL (psycopg2) — реляционная БД для пользователей
|
- PostgreSQL (psycopg2) — реляционная БД для пользователей
|
||||||
- Neo4j — графовая БД для фильмов
|
- Neo4j — графовая БД для фильмов
|
||||||
|
- Apache AGE — графовая модель поверх PostgreSQL для фильмов
|
||||||
- python-dotenv — загрузка параметров подключения из файла `.env`
|
- python-dotenv — загрузка параметров подключения из файла `.env`
|
||||||
|
|
||||||
### Скриншоты
|
### Скриншоты
|
||||||
@@ -24,6 +25,7 @@
|
|||||||
- Python 3.10+
|
- Python 3.10+
|
||||||
- Установленный PostgreSQL и доступ к серверу БД
|
- Установленный PostgreSQL и доступ к серверу БД
|
||||||
- Установленный Neo4j и загруженный обучающий граф фильмов (Movie Graph example)
|
- Установленный Neo4j и загруженный обучающий граф фильмов (Movie Graph example)
|
||||||
|
- Для Apache AGE: установленное расширение `age` в PostgreSQL и доступ к базе, где оно включено
|
||||||
|
|
||||||
### Настройка окружения
|
### Настройка окружения
|
||||||
|
|
||||||
@@ -43,6 +45,14 @@ NEO_ACTIVE=false # включить Neo4j-часть приложения,
|
|||||||
NEO_USER=neo4j
|
NEO_USER=neo4j
|
||||||
NEO_PASS=your_password
|
NEO_PASS=your_password
|
||||||
NEO_HOST=neo4j://localhost:7687
|
NEO_HOST=neo4j://localhost:7687
|
||||||
|
|
||||||
|
AGE_ACTIVE=false # включить Apache AGE, установив true
|
||||||
|
AGE_USER=postgres
|
||||||
|
AGE_PASS=your_password
|
||||||
|
AGE_HOST=localhost
|
||||||
|
AGE_PORT=5432
|
||||||
|
AGE_DB=university
|
||||||
|
AGE_GRAPH_NAME=movie_graph
|
||||||
```
|
```
|
||||||
|
|
||||||
2. Создайте и активируйте виртуальное окружение:
|
2. Создайте и активируйте виртуальное окружение:
|
||||||
@@ -77,4 +87,4 @@ pip install -r requirements.txt
|
|||||||
py main.py
|
py main.py
|
||||||
```
|
```
|
||||||
|
|
||||||
Приложение подключится к PostgreSQL, создаст БД/таблицу и тестовые данные (если их ещё нет), выполнит запрос к Neo4j (если активно) и отобразит результаты в двух окнах.
|
Приложение подключится к PostgreSQL, создаст БД/таблицу и тестовые данные (если их еще нет), выполнит запрос к Neo4j (если активно) и/или к Apache AGE (если активно), а затем отобразит результаты в отдельных окнах.
|
||||||
@@ -1,245 +1,301 @@
|
|||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
import psycopg2
|
import psycopg2
|
||||||
|
from dotenv import load_dotenv
|
||||||
|
from neo4j import GraphDatabase
|
||||||
from psycopg2 import sql
|
from psycopg2 import sql
|
||||||
from psycopg2.errors import DuplicateDatabase
|
from psycopg2.errors import DuplicateDatabase
|
||||||
from neo4j import GraphDatabase
|
|
||||||
from PyQt5.QtWidgets import QApplication, QMainWindow, QGridLayout, QWidget, QTableWidget, QTableWidgetItem, \
|
|
||||||
QHeaderView, QVBoxLayout, QLabel
|
|
||||||
from PyQt5.QtCore import QSize, Qt
|
from PyQt5.QtCore import QSize, Qt
|
||||||
|
from PyQt5.QtWidgets import QApplication, QGridLayout, QHeaderView, QMainWindow, QTableWidget, QTableWidgetItem, QWidget
|
||||||
|
|
||||||
import os
|
|
||||||
from dotenv import load_dotenv
|
|
||||||
load_dotenv()
|
load_dotenv()
|
||||||
|
|
||||||
NEO_USER = os.getenv('NEO_USER')
|
POSTGRES_USER = os.getenv("POSTGRES_USER")
|
||||||
NEO_PASS = os.getenv('NEO_PASS')
|
POSTGRES_DB = os.getenv("POSTGRES_DB")
|
||||||
NEO_HOST = os.getenv('NEO_HOST')
|
POSTGRES_PASS = os.getenv("POSTGRES_PASS")
|
||||||
|
POSTGRES_HOST = os.getenv("POSTGRES_HOST")
|
||||||
|
POSTGRES_PORT = os.getenv("POSTGRES_PORT")
|
||||||
|
|
||||||
POSTGRES_USER = os.getenv('POSTGRES_USER')
|
NEO_ACTIVE = os.getenv("NEO_ACTIVE", "false").lower() == "true"
|
||||||
POSTGRES_DB = os.getenv('POSTGRES_DB')
|
NEO_USER = os.getenv("NEO_USER")
|
||||||
POSTGRES_PASS = os.getenv('POSTGRES_PASS')
|
NEO_PASS = os.getenv("NEO_PASS")
|
||||||
POSTGRES_HOST = os.getenv('POSTGRES_HOST')
|
NEO_HOST = os.getenv("NEO_HOST")
|
||||||
POSTGRES_PORT = os.getenv('POSTGRES_PORT')
|
|
||||||
|
|
||||||
def connect(config):
|
AGE_ACTIVE = os.getenv("AGE_ACTIVE", "false").lower() == "true"
|
||||||
""" Connect to the PostgreSQL database server """
|
AGE_USER = os.getenv("AGE_USER", POSTGRES_USER)
|
||||||
|
AGE_PASS = os.getenv("AGE_PASS", POSTGRES_PASS)
|
||||||
|
AGE_HOST = os.getenv("AGE_HOST", POSTGRES_HOST)
|
||||||
|
AGE_PORT = os.getenv("AGE_PORT", POSTGRES_PORT)
|
||||||
|
AGE_DB = os.getenv("AGE_DB", POSTGRES_DB)
|
||||||
|
AGE_GRAPH_NAME = os.getenv("AGE_GRAPH_NAME", "movie_graph")
|
||||||
|
|
||||||
|
USER_SEED_DATA = [
|
||||||
|
(1, "Ivan", 15),
|
||||||
|
(2, "Igor", 22),
|
||||||
|
(3, "Alex", 16),
|
||||||
|
(4, "Anna", 40),
|
||||||
|
(5, "Inna", 30),
|
||||||
|
]
|
||||||
|
|
||||||
|
def connect_postgres(dbname):
|
||||||
|
conn = psycopg2.connect(
|
||||||
|
dbname=dbname,
|
||||||
|
user=POSTGRES_USER,
|
||||||
|
password=POSTGRES_PASS,
|
||||||
|
host=POSTGRES_HOST,
|
||||||
|
port=POSTGRES_PORT,
|
||||||
|
)
|
||||||
|
conn.autocommit = True
|
||||||
|
return conn
|
||||||
|
|
||||||
|
|
||||||
|
def create_database(admin_conn, database_name):
|
||||||
try:
|
try:
|
||||||
# connecting to the PostgreSQL server
|
with admin_conn.cursor() as cur:
|
||||||
with psycopg2.connect(**config) as conn:
|
cur.execute(sql.SQL("CREATE DATABASE {}").format(sql.Identifier(database_name)))
|
||||||
print('Connected to the PostgreSQL server.')
|
print(f"Database '{database_name}' created.")
|
||||||
return conn
|
except DuplicateDatabase:
|
||||||
|
print(f"Database '{database_name}' already exists.")
|
||||||
|
|
||||||
|
|
||||||
|
def create_users_table(conn):
|
||||||
|
try:
|
||||||
|
with conn.cursor() as cur:
|
||||||
|
cur.execute(
|
||||||
|
"""
|
||||||
|
CREATE TABLE IF NOT EXISTS users (
|
||||||
|
id INT PRIMARY KEY,
|
||||||
|
name VARCHAR(255),
|
||||||
|
age INT
|
||||||
|
)
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
print("Users table is ready.")
|
||||||
except (psycopg2.DatabaseError, Exception) as error:
|
except (psycopg2.DatabaseError, Exception) as error:
|
||||||
print(error)
|
print(error)
|
||||||
|
|
||||||
|
|
||||||
def createUsersDB(config):
|
def select_users(conn):
|
||||||
""" Connect to the PostgreSQL database server """
|
|
||||||
try:
|
try:
|
||||||
# connecting to the PostgreSQL server
|
with conn.cursor() as cur:
|
||||||
conn.autocommit = True
|
cur.execute("SELECT id, name, age FROM users ORDER BY id")
|
||||||
cur = conn.cursor()
|
return cur.fetchall()
|
||||||
cur.execute(sql.SQL("CREATE DATABASE {}").format(
|
|
||||||
sql.Identifier('university'))
|
|
||||||
)
|
|
||||||
print('db created')
|
|
||||||
except DuplicateDatabase:
|
|
||||||
print('db already exists')
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
def createuUiversityDB(config):
|
|
||||||
""" Connect to the PostgreSQL database server """
|
|
||||||
try:
|
|
||||||
# connecting to the PostgreSQL server
|
|
||||||
conn.autocommit = True
|
|
||||||
cur = conn.cursor()
|
|
||||||
cur.execute(sql.SQL("CREATE DATABASE {}").format(
|
|
||||||
sql.Identifier('university'))
|
|
||||||
)
|
|
||||||
print('db created')
|
|
||||||
except DuplicateDatabase:
|
|
||||||
print('db already exists')
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
def createUsersTable(config):
|
|
||||||
""" Connect to the PostgreSQL database server """
|
|
||||||
try:
|
|
||||||
# connecting to the PostgreSQL server
|
|
||||||
conn.autocommit = True
|
|
||||||
cur = conn.cursor()
|
|
||||||
cur.execute(sql.SQL("CREATE TABLE IF NOT EXISTS users(id int, name varchar(255), age int)"))
|
|
||||||
print('table created')
|
|
||||||
except (psycopg2.DatabaseError, Exception) as error:
|
|
||||||
print(error)
|
|
||||||
|
|
||||||
|
|
||||||
def selectUsers(config):
|
|
||||||
try:
|
|
||||||
cur = conn.cursor()
|
|
||||||
cur.execute(sql.SQL("SELECT * FROM users"))
|
|
||||||
return cur.fetchall()
|
|
||||||
except (psycopg2.DatabaseError, Exception) as error:
|
except (psycopg2.DatabaseError, Exception) as error:
|
||||||
print(error)
|
print(error)
|
||||||
return []
|
return []
|
||||||
|
|
||||||
|
|
||||||
def insertUsersData(config, values):
|
def insert_users_data(conn, values):
|
||||||
""" Connect to the PostgreSQL database server """
|
|
||||||
try:
|
try:
|
||||||
conn.autocommit = True
|
existing_users = select_users(conn)
|
||||||
cur = conn.cursor()
|
if existing_users:
|
||||||
|
print("Users are already seeded.")
|
||||||
|
return
|
||||||
|
|
||||||
users = selectUsers(conn)
|
with conn.cursor() as cur:
|
||||||
|
args = ",".join(cur.mogrify("(%s,%s,%s)", value).decode("utf-8") for value in values)
|
||||||
if len(users) == 0:
|
cur.execute("INSERT INTO users (id, name, age) VALUES " + args)
|
||||||
args = ','.join(cur.mogrify("(%s,%s,%s)", i).decode('utf-8')
|
print("Users inserted.")
|
||||||
for i in values)
|
|
||||||
|
|
||||||
# executing the sql statement
|
|
||||||
cur.execute("INSERT INTO users VALUES " + (args))
|
|
||||||
print('inserted')
|
|
||||||
else:
|
|
||||||
print('already inserted')
|
|
||||||
print(users)
|
|
||||||
except (psycopg2.DatabaseError, Exception) as error:
|
except (psycopg2.DatabaseError, Exception) as error:
|
||||||
print(error)
|
print(error)
|
||||||
|
|
||||||
|
|
||||||
|
def fetch_neo_movies():
|
||||||
|
uri = NEO_HOST
|
||||||
|
auth = (NEO_USER, NEO_PASS)
|
||||||
|
|
||||||
|
with GraphDatabase.driver(uri, auth=auth) as driver:
|
||||||
|
records, _, _ = driver.execute_query(
|
||||||
|
"""
|
||||||
|
MATCH (movie:Movie)
|
||||||
|
WHERE movie.released >= 1990 AND movie.released < 2000
|
||||||
|
RETURN movie.title AS title
|
||||||
|
ORDER BY title
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
return [record["title"] for record in records]
|
||||||
|
|
||||||
|
|
||||||
|
def create_age_movies(cur):
|
||||||
|
cur.execute("CREATE EXTENSION IF NOT EXISTS age;")
|
||||||
|
cur.execute("LOAD 'age';")
|
||||||
|
cur.execute('SET search_path = ag_catalog, "$user", public;')
|
||||||
|
|
||||||
|
cur.execute("SELECT 1 FROM ag_catalog.ag_graph WHERE name = %s", (AGE_GRAPH_NAME,))
|
||||||
|
if cur.fetchone() is None:
|
||||||
|
cur.execute("SELECT * FROM ag_catalog.create_graph(%s)", (AGE_GRAPH_NAME,))
|
||||||
|
|
||||||
|
cur.execute(
|
||||||
|
sql.SQL("SELECT * FROM cypher({}, $$ {} $$) AS (seeded agtype)").format(
|
||||||
|
sql.Literal(AGE_GRAPH_NAME),
|
||||||
|
sql.SQL('''
|
||||||
|
MERGE (matrix:Movie {title: 'The Matrix', released: 1999})
|
||||||
|
MERGE (apollo:Movie {title: 'Apollo 13', released: 1995})
|
||||||
|
MERGE (toy_story:Movie {title: 'Toy Story', released: 1995})
|
||||||
|
RETURN matrix.title AS seeded
|
||||||
|
'''),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def select_age_movies(cur):
|
||||||
|
cur.execute(
|
||||||
|
sql.SQL("SELECT * FROM cypher({}, $$ {} $$) AS (title agtype)").format(
|
||||||
|
sql.Literal(AGE_GRAPH_NAME),
|
||||||
|
sql.SQL('''
|
||||||
|
MATCH (movie:Movie)
|
||||||
|
WHERE movie.released >= 1990 AND movie.released < 2000
|
||||||
|
RETURN movie.title AS title
|
||||||
|
ORDER BY title
|
||||||
|
'''),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return [str(row[0]).strip('"') for row in cur.fetchall()]
|
||||||
|
|
||||||
|
|
||||||
|
def load_users():
|
||||||
|
admin_conn = connect_postgres("postgres")
|
||||||
|
try:
|
||||||
|
create_database(admin_conn, POSTGRES_DB)
|
||||||
|
finally:
|
||||||
|
admin_conn.close()
|
||||||
|
|
||||||
|
app_conn = connect_postgres(POSTGRES_DB)
|
||||||
|
try:
|
||||||
|
create_users_table(app_conn)
|
||||||
|
insert_users_data(app_conn, USER_SEED_DATA)
|
||||||
|
return select_users(app_conn)
|
||||||
|
finally:
|
||||||
|
app_conn.close()
|
||||||
|
|
||||||
|
|
||||||
class MainWindow(QMainWindow):
|
class MainWindow(QMainWindow):
|
||||||
# Override class constructor
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
# You must call the super class method
|
super().__init__()
|
||||||
QMainWindow.__init__(self)
|
|
||||||
|
|
||||||
self.setMinimumSize(QSize(640, 480)) # Set sizes
|
self.setMinimumSize(QSize(640, 480))
|
||||||
self.setWindowTitle("postgre") # Set the window title
|
self.setWindowTitle("postgres")
|
||||||
central_widget = QWidget(self) # Create a central widget
|
|
||||||
self.setCentralWidget(central_widget) # Install the central widget
|
|
||||||
|
|
||||||
grid_layout = QGridLayout(self) # Create QGridLayout
|
central_widget = QWidget(self)
|
||||||
central_widget.setLayout(grid_layout) # Set this layout in central widget
|
self.setCentralWidget(central_widget)
|
||||||
|
|
||||||
self.table = QTableWidget(self) # Create a table
|
grid_layout = QGridLayout(self)
|
||||||
self.table.setColumnCount(3) # Set three columns
|
central_widget.setLayout(grid_layout)
|
||||||
self.table.setRowCount(1) # and one row
|
|
||||||
|
self.table = QTableWidget(self)
|
||||||
|
self.table.setColumnCount(3)
|
||||||
|
self.table.setRowCount(0)
|
||||||
self.table.horizontalHeader().setSectionResizeMode(1, QHeaderView.Stretch)
|
self.table.horizontalHeader().setSectionResizeMode(1, QHeaderView.Stretch)
|
||||||
# Set the table headers
|
|
||||||
self.table.setHorizontalHeaderLabels(["ID", "Name", "Age"])
|
self.table.setHorizontalHeaderLabels(["ID", "Name", "Age"])
|
||||||
|
self.table.horizontalHeaderItem(0).setToolTip("ID")
|
||||||
# Set the tooltips to headings
|
self.table.horizontalHeaderItem(1).setToolTip("Name")
|
||||||
self.table.horizontalHeaderItem(0).setToolTip("ID ")
|
self.table.horizontalHeaderItem(2).setToolTip("Age")
|
||||||
self.table.horizontalHeaderItem(1).setToolTip("Name ")
|
|
||||||
self.table.horizontalHeaderItem(2).setToolTip("Age ")
|
|
||||||
|
|
||||||
# Set the alignment to the headers
|
|
||||||
self.table.horizontalHeaderItem(0).setTextAlignment(Qt.AlignHCenter)
|
self.table.horizontalHeaderItem(0).setTextAlignment(Qt.AlignHCenter)
|
||||||
self.table.horizontalHeaderItem(1).setTextAlignment(Qt.AlignHCenter)
|
self.table.horizontalHeaderItem(1).setTextAlignment(Qt.AlignHCenter)
|
||||||
self.table.horizontalHeaderItem(2).setTextAlignment(Qt.AlignHCenter)
|
self.table.horizontalHeaderItem(2).setTextAlignment(Qt.AlignHCenter)
|
||||||
|
|
||||||
# Do the resize of the columns by content
|
grid_layout.addWidget(self.table, 0, 0)
|
||||||
self.table.resizeColumnsToContents()
|
|
||||||
|
|
||||||
grid_layout.addWidget(self.table, 0, 0) # Adding the table to the grid
|
|
||||||
|
|
||||||
def load_data(self, users_data):
|
def load_data(self, users_data):
|
||||||
for i, user in enumerate(users_data):
|
self.table.setRowCount(len(users_data))
|
||||||
row_number = self.table.rowCount()-1
|
for row_number, user in enumerate(users_data):
|
||||||
if len(users_data) != self.table.rowCount():
|
|
||||||
self.table.insertRow(row_number)
|
|
||||||
self.table.setItem(row_number, 0, QTableWidgetItem(str(user[0])))
|
self.table.setItem(row_number, 0, QTableWidgetItem(str(user[0])))
|
||||||
self.table.setItem(row_number, 1, QTableWidgetItem(str(user[1])))
|
self.table.setItem(row_number, 1, QTableWidgetItem(str(user[1])))
|
||||||
self.table.setItem(row_number, 2, QTableWidgetItem(str(user[2])))
|
self.table.setItem(row_number, 2, QTableWidgetItem(str(user[2])))
|
||||||
|
self.table.resizeColumnsToContents()
|
||||||
|
|
||||||
|
|
||||||
class NeoWindow(QWidget):
|
class GraphWindow(QWidget):
|
||||||
def __init__(self, parent_window):
|
def __init__(self, title, parent_window):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
|
||||||
self.setWindowTitle("neo4j")
|
self.setWindowTitle(title)
|
||||||
self.resize(300, 250)
|
|
||||||
|
|
||||||
grid_layout = QGridLayout(self) # Create QGridLayout
|
grid_layout = QGridLayout(self)
|
||||||
self.setLayout(grid_layout) # Set this layout in central widget
|
self.setLayout(grid_layout)
|
||||||
|
|
||||||
self.table = QTableWidget(self)
|
self.table = QTableWidget(self)
|
||||||
self.table.setColumnCount(1)
|
self.table.setColumnCount(1)
|
||||||
self.table.setRowCount(1)
|
self.table.setRowCount(0)
|
||||||
|
|
||||||
self.table.setHorizontalHeaderLabels(["Info"])
|
self.table.setHorizontalHeaderLabels(["Info"])
|
||||||
|
|
||||||
# Set the tooltips to headings
|
|
||||||
self.table.horizontalHeaderItem(0).setToolTip("Info")
|
self.table.horizontalHeaderItem(0).setToolTip("Info")
|
||||||
|
|
||||||
# Set the alignment to the headers
|
|
||||||
self.table.horizontalHeaderItem(0).setTextAlignment(Qt.AlignHCenter)
|
self.table.horizontalHeaderItem(0).setTextAlignment(Qt.AlignHCenter)
|
||||||
|
|
||||||
|
grid_layout.addWidget(self.table, 0, 0)
|
||||||
grid_layout.addWidget(self.table, 0, 0) # Adding the table to the grid
|
|
||||||
|
|
||||||
# Position the window to the right of the parent window
|
|
||||||
self.position_relative_to_parent(parent_window)
|
self.position_relative_to_parent(parent_window)
|
||||||
|
|
||||||
def position_relative_to_parent(self, parent_window):
|
def position_relative_to_parent(self, parent_window):
|
||||||
# Get the geometry of the parent window
|
|
||||||
parent_frame = parent_window.frameGeometry()
|
parent_frame = parent_window.frameGeometry()
|
||||||
|
self.move(parent_frame.x() + parent_frame.width() + 5, parent_frame.y())
|
||||||
|
|
||||||
# Calculate new position (right of parent window with a small gap)
|
def load_data(self, rows):
|
||||||
new_x = parent_frame.x() + parent_frame.width() + 5
|
self.table.setRowCount(len(rows))
|
||||||
new_y = parent_frame.y()
|
for row_number, item in enumerate(rows):
|
||||||
|
self.table.setItem(row_number, 0, QTableWidgetItem(str(item)))
|
||||||
# Move the window to the new position
|
|
||||||
self.move(new_x, new_y)
|
|
||||||
|
|
||||||
def load_data(self, neo_data):
|
|
||||||
for i, node_data in enumerate(neo_data):
|
|
||||||
row_number = self.table.rowCount()-1
|
|
||||||
if len(neo_data) != self.table.rowCount():
|
|
||||||
self.table.insertRow(row_number)
|
|
||||||
self.table.setItem(row_number, 0, QTableWidgetItem(str(node_data[0]["title"])))
|
|
||||||
|
|
||||||
self.table.resizeColumnsToContents()
|
self.table.resizeColumnsToContents()
|
||||||
|
self.table.resizeRowsToContents()
|
||||||
|
|
||||||
|
margins = self.layout().contentsMargins()
|
||||||
|
width = (
|
||||||
|
self.table.verticalHeader().width()
|
||||||
|
+ self.table.columnWidth(0)
|
||||||
|
+ self.table.frameWidth() * 2
|
||||||
|
+ margins.left()
|
||||||
|
+ margins.right()
|
||||||
|
+ 24
|
||||||
|
)
|
||||||
|
height = (
|
||||||
|
self.table.horizontalHeader().height()
|
||||||
|
+ sum(self.table.rowHeight(row) for row in range(self.table.rowCount()))
|
||||||
|
+ self.table.frameWidth() * 2
|
||||||
|
+ margins.top()
|
||||||
|
+ margins.bottom()
|
||||||
|
+ 24
|
||||||
|
)
|
||||||
|
|
||||||
|
self.resize(min(max(width, 220), 700), min(max(height, 120), 500))
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
import sys
|
users = load_users()
|
||||||
|
|
||||||
conn = psycopg2.connect(
|
|
||||||
"user={0} password={1} host={2} port={3}".format(
|
|
||||||
POSTGRES_USER,
|
|
||||||
POSTGRES_PASS,
|
|
||||||
POSTGRES_HOST,
|
|
||||||
POSTGRES_PORT,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
createuUiversityDB(conn)
|
|
||||||
|
|
||||||
conn = psycopg2.connect("user={0} dbname={1} password={2} host={3} port={4}".format(POSTGRES_USER, POSTGRES_DB,
|
|
||||||
POSTGRES_PASS, POSTGRES_HOST, POSTGRES_PORT))
|
|
||||||
|
|
||||||
createUsersTable(conn)
|
|
||||||
|
|
||||||
values = [(1, 'Ivan', 15), (2, 'Igor', 22), (3, 'Alex', 16), (4, 'Anna', 40), (5, 'Inna', 30)]
|
|
||||||
|
|
||||||
insertUsersData(conn, values)
|
|
||||||
|
|
||||||
users = selectUsers(conn)
|
|
||||||
|
|
||||||
app = QApplication(sys.argv)
|
app = QApplication(sys.argv)
|
||||||
mw = MainWindow()
|
main_window = MainWindow()
|
||||||
mw.load_data(users)
|
main_window.load_data(users)
|
||||||
mw.show()
|
main_window.show()
|
||||||
|
|
||||||
if os.getenv('NEO_ACTIVE') == 'true':
|
graph_windows = []
|
||||||
URI = NEO_HOST
|
|
||||||
AUTH = (NEO_USER, NEO_PASS)
|
|
||||||
|
|
||||||
with GraphDatabase.driver(URI, auth=AUTH) as driver:
|
if NEO_ACTIVE:
|
||||||
records, summary, keys = driver.execute_query(
|
try:
|
||||||
"MATCH (nineties:Movie) WHERE nineties.released >= 1990 AND nineties.released < 2000 RETURN nineties"
|
neo_window = GraphWindow("neo4j", main_window)
|
||||||
)
|
neo_window.load_data(fetch_neo_movies())
|
||||||
|
neo_window.show()
|
||||||
|
graph_windows.append(neo_window)
|
||||||
|
except Exception as error:
|
||||||
|
print(f"Neo4j load failed: {error}")
|
||||||
|
|
||||||
neo_window = NeoWindow(mw)
|
if AGE_ACTIVE:
|
||||||
neo_window.load_data(records)
|
try:
|
||||||
neo_window.show()
|
with psycopg2.connect(
|
||||||
|
dbname=AGE_DB,
|
||||||
|
user=AGE_USER,
|
||||||
|
password=AGE_PASS,
|
||||||
|
host=AGE_HOST,
|
||||||
|
port=AGE_PORT,
|
||||||
|
) as age_conn:
|
||||||
|
age_conn.autocommit = True
|
||||||
|
|
||||||
|
with age_conn.cursor() as cur:
|
||||||
|
create_age_movies(cur)
|
||||||
|
age_movies = select_age_movies(cur)
|
||||||
|
|
||||||
|
age_window = GraphWindow("apache age", main_window)
|
||||||
|
age_window.load_data(age_movies)
|
||||||
|
age_window.show()
|
||||||
|
graph_windows.append(age_window)
|
||||||
|
except Exception as error:
|
||||||
|
print(f"Apache AGE load failed: {error}")
|
||||||
|
|
||||||
sys.exit(app.exec())
|
sys.exit(app.exec())
|
||||||
Reference in New Issue
Block a user