Finished schema creation

Added Navbar functionality
Added header
This commit is contained in:
Josako
2024-04-25 07:51:18 +02:00
parent 6de87e1509
commit 396de4e079
8 changed files with 166 additions and 66 deletions

View File

@@ -2,6 +2,7 @@ import os
from flask import Flask
from .extensions import db, migrate, bcrypt, bootstrap, jwt
from .models.user import User, Tenant
from .models.document import Document, DocumentLanguage, DocumentVersion
def create_app(config_file=None):

View File

@@ -1,37 +1,38 @@
from ..extensions import db
from .user import User, Tenant
class Document(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(100), nullable=False)
tenant_id = db.Column(db.Integer, db.ForeignKey('tenant.id'), nullable=False)
tenant_id = db.Column(db.Integer, db.ForeignKey(Tenant.id), nullable=False)
valid_from = db.Column(db.DateTime, nullable=True)
valid_to = db.Column(db.DateTime, nullable=True)
# Versioning Information
created_at = db.Column(db.DateTime, nullable=False, server_default=db.func.now())
created_by = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
created_by = db.Column(db.Integer, db.ForeignKey(User.id), nullable=False)
updated_at = db.Column(db.DateTime, nullable=False, server_default=db.func.now(), onupdate=db.func.now())
updated_by = db.Column(db.Integer, db.ForeignKey('user.id'))
updated_by = db.Column(db.Integer, db.ForeignKey(User.id))
class DocumentLanguage(db.Model):
id = db.Column(db.Integer, primary_key=True)
document_id = db.Column(db.Integer, db.ForeignKey('document.id'), nullable=False)
document_id = db.Column(db.Integer, db.ForeignKey(Document.id), nullable=False)
language = db.Column(db.String(2), nullable=False)
# Versioning Information
created_at = db.Column(db.DateTime, nullable=False, server_default=db.func.now())
created_by = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
created_by = db.Column(db.Integer, db.ForeignKey(User.id), nullable=False)
class DocumentVersion(db.Model):
id = db.Column(db.Integer, primary_key=True)
doc_lang_id = db.Column(db.Integer, db.ForeignKey('document_language.id'), nullable=False)
doc_lang_id = db.Column(db.Integer, db.ForeignKey(DocumentLanguage.id), nullable=False)
url = db.Column(db.String(200), nullable=True)
embeddings = db.Column(db.PickleType, nullable=True)
# Versioning Information
created_at = db.Column(db.DateTime, nullable=False, server_default=db.func.now())
created_by = db.Column(db.Integer, db.ForeignKey('user.id'))
created_by = db.Column(db.Integer, db.ForeignKey(User.id))

View File

@@ -24,7 +24,8 @@
<body class="presentation-page bg-gray-200">
{% include 'navbar.html' %}
{% with messages = get_flashed_messages()%}
{% include 'header.html' %}
{% with messages = get_flashed_messages() %}
{% if messages%}
{% for message in messages%}
<p>{{message}}</p>

View File

@@ -0,0 +1,13 @@
<header class="header-2">
<div class="page-header min-vh-75" style="background-image: url({{url_for('static', filename='/assets/img/EveAI_bg1.jpg')}})" loading="lazy">
<span class="mask bg-gradient-primary opacity-4"></span>
<div class="container">
<div class="row">
<div class="col-lg-7 text-center mx-auto">
<h1 class="text-white pt-3 mt-n5">EveAI Virtual Assistant</h1>
<p class="lead text-white mt-3 px-5">Enhance Customer Interaction with AI</p>
</div>
</div>
</div>
</div>
</header>

View File

@@ -1,59 +1,46 @@
<!-- Navbar Light -->
<nav
class="navbar navbar-expand-lg navbar-light bg-white z-index-3 py-3">
<div class="container">
<a class="navbar-brand" href="" rel="tooltip" title="Generated by Kobe & Pieter @ Flow IT" data-placement="bottom" target="_blank">
EveAI
</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navigation" aria-controls="navigation" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navigation">
<ul class="navbar-nav navbar-nav-hover mx-auto">
<li class="nav-item px-3">
<a class="nav-link">
User Mgmt
</a>
<ul class="dropdown-menu" aria-labelledby="dropdownMenuButton">
<li>
<a class="dropdown-item" href="/user/tenant">
Tenant Registration
</a>
</li>
<li>
<a class="dropdown-item" href="/user/user">
User Registration
</a>
</li>
</ul>
</li>
<li class="nav-item px-3">
<a class="nav-link">
Document Mgmt
</a>
</li>
<li class="nav-item px-3">
<a class="nav-link">
Interaction Mgmt
</a>
</li>
<li class="nav-item px-3">
<a class="nav-link ">
Login
</a>
</li>
</ul>
<ul class="navbar-nav ms-auto">
<button class="btn bg-gradient-primary mb-0">Buy Now</button>
</ul>
<div class="navbar navbar-expand-lg navbar-light bg-white z-index-3 py-3">
<div class="container position-sticky z-index-sticky top-0">
<div class="row">
<div class="col-12">
<nav class="navbar navbar-expand-lg blur border-radius-xl top-0 z-index-fixed shadow position-absolute my-3 py-2 start-0 end-0 mx-4">
<div class="container-fluid px-0">
<a class="navbar-brand font-weight-bolder ms-sm-3 d-none d-md-block" href="https://www.flow-it.net" rel="tooltip" title="Idee Generated & Implemented by Flow IT" data-placement="bottom">
EveAI
</a>
</div>
<div class="collapse navbar-collapse w-100 pt-3 pb-2 py-lg-0" id="navigation">
<ul class="navbar-nav navbar-nav-hover mx-auto">
<li class="nav-item dropdown dropdown-hover mx-2">
<a role="button" class="nav-link ps-2 d-flex cursor-pointer align-items-center" id="dropdownMenuUser" data-bs-toggle="dropdown" aria-expanded="false">
<i class="material-icons opacity-6 me-2 text-md">contacts</i>
User Mgmt
<img src="{{url_for('static', filename='/assets/img/down-arrow-dark.svg')}}" alt="down-arrow" class="arrow ms-2 d-lg-block d-none">
<img src="{{url_for('static', filename='/assets/img/down-arrow-dark.svg')}}" alt="down-arrow" class="arrow ms-1 d-lg-none d-block ms-auto">
</a>
<div class="dropdown-menu dropdown-menu-animation ms-n3 dropdown-md p-3 border-radius-xl mt-0 mt-lg-3" aria-labelledby="dropdownMenuPages">
<div class="d-none d-lg-flex">
<ul class="list-group w-100">
<li class="nav-item dropdown dropdown-hover dropdown-subitem list-group-item border-0 p-0">
<a class="dropdown-item ps-3 border-radius-md mb-1" href="/user/tenant">
<span>Tenant Registration</span>
</a>
<a class="dropdown-item ps-3 border-radius-md mb-1" href="/user/user">
<span>User Registration</span>
</a>
</li>
</ul>
</div>
</div>
</li>
</ul>
</div>
</nav>
</div>
</div>
</div>
</nav>
</div>
<!-- End Navbar -->
</div>

View File

@@ -0,0 +1,89 @@
"""Database related functions"""
from os import popen
from sqlalchemy import text
from sqlalchemy.schema import CreateSchema
from sqlalchemy.exc import InternalError
from sqlalchemy.orm import sessionmaker, scoped_session
from flask_migrate import heads
from ..extensions import db, migrate
class Database:
"""used for managing tenant databases related operations"""
def __init__(self, tenant: str) -> None:
self.schema = str(tenant)
def get_engine(self):
"""create new schema engine"""
return db.engine.execution_options(
schema_translate_map={None: self.schema}
)
def get_session(self):
"""To get session of tenant/public database schema for quick use
returns:
session: session of tenant/public database schema
"""
return scoped_session(
sessionmaker(bind=self.get_engine(), expire_on_commit=True)
)
def create_schema(self):
"""create new database schema, mostly used on tenant creation"""
try:
db.session.execute(CreateSchema(self.schema))
db.session.commit()
except InternalError:
db.session.rollback()
db.session.close()
def create_tables(self):
"""create tables in for schema"""
db.metadata.create_all(self.get_engine())
def switch_schema(self):
"""switch between tenant/public database schema"""
db.session.execute(text(f'set search_path to "{self.schema}"'))
db.session.commit()
def migrate_tenant_schema(self):
"""migrate tenant database schema for new tenant"""
# Get the current revision for a database.
# NOTE: using popen may have a minor performance impact on the application
# you can store it in a different table in public schema and use it from there
# may be a faster approach
# last_revision = heads(directory="migrations/tenant", verbose=True, resolve_dependencies=False)
last_revision = popen(".venv/bin/flask db heads -d migrations/tenant").read()
print("LAST REVISION")
print(last_revision)
last_revision = last_revision.splitlines()[-1].split(" ")[0]
# creating revision table in tenant schema
session = self.get_session()
session.execute(
text(
f'CREATE TABLE "{self.schema}".alembic_version (version_num '
"VARCHAR(32) NOT NULL)"
)
)
session.commit()
# Insert last revision to alembic_version table
session.execute(
text(
f'INSERT INTO "{self.schema}".alembic_version (version_num) '
"VALUES (:version)"
),
{"version": last_revision},
)
session.commit()
session.close()
def create_tenant_schema(self):
"""create tenant used for creating new schema and its tables"""
self.create_schema()
self.create_tables()
self.migrate_tenant_schema()

View File

@@ -4,6 +4,7 @@ from flask import request, redirect, url_for, flash, render_template, Blueprint
from ..models.user import User, Tenant
from ..extensions import db, bcrypt
from .user_forms import TenantForm, UserForm
from ..utils.database import Database
user_bp = Blueprint('user_bp', __name__, url_prefix='/user')
@@ -31,9 +32,9 @@ def tenant():
monthly = request.form.get('allowed_monthly_interactions')
if lic_start != '':
new_tenant.license_start_date = dt.strptime(lic_start, '%d-%m-%Y')
new_tenant.license_start_date = dt.strptime(lic_start, '%Y-%m-%d')
if lic_end != '':
new_tenant.license_end_date = dt.strptime(lic_end, '%d-%m-%Y')
new_tenant.license_end_date = dt.strptime(lic_end, '%Y-%m-%d')
if monthly != '':
new_tenant.allowed_monthly_interactions = int(monthly)
@@ -43,13 +44,17 @@ def tenant():
new_tenant.updated_at = timestamp
# Add the new tenant to the database and commit the changes
try:
db.session.add(new_tenant)
db.session.commit()
except Exception as e:
error = e.args
# Create schema for new tenant
if error is None:
print(new_tenant.id)
Database(new_tenant.id).create_tenant_schema()
flash(error) if error else flash('Tenant added successfully.')
form = TenantForm()
@@ -81,7 +86,8 @@ def user():
password_hash = bcrypt.generate_password_hash(password).decode('utf-8')
# Create new user if there is no error
new_user = User(user_name=username, email=email, password=password_hash, first_name=first_name, last_name=last_name)
new_user = User(user_name=username, email=email, password=password_hash, first_name=first_name,
last_name=last_name)
# Handle optional attributes
new_user.is_active = bool(request.form.get('is_active'))

View File

@@ -4,6 +4,7 @@ from logging.config import fileConfig
from flask import current_app
from alembic import context
from sqlalchemy import NullPool, engine_from_config, text
from eveai_app.models.user import Tenant
@@ -92,6 +93,7 @@ def run_migrations_online():
)
with connectable.connect() as connection:
print(tenants)
for tenant in tenants:
logger.info(f"Migrating tenant: {tenant}")
# set search path on the connection, which ensures that
@@ -99,7 +101,7 @@ def run_migrations_online():
# in terms of this schema by default
connection.execute(text(f'SET search_path TO "{tenant}"'))
# in SQLAlchemy v2+ the search path change needs to be committed
# connection.commit()
connection.commit()
# make use of non-supported SQLAlchemy attribute to ensure
# the dialect reflects tables in terms of the current tenant name