diff --git a/common/utils/database.py b/common/utils/database.py index c7e40fc..6ae77cb 100644 --- a/common/utils/database.py +++ b/common/utils/database.py @@ -60,7 +60,7 @@ class Database: # 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() + last_revision = popen("scripts/db_heads.sh -d migrations/tenant").read() print("LAST REVISION") print(last_revision) last_revision = last_revision.splitlines()[-1].split(" ")[0] diff --git a/common/utils/view_assistants.py b/common/utils/view_assistants.py index a7ea7aa..6780fa2 100644 --- a/common/utils/view_assistants.py +++ b/common/utils/view_assistants.py @@ -1,3 +1,6 @@ +from flask import flash + + def prepare_table(model_objects, column_names): """ Converts a list of SQLAlchemy model objects into a list of dictionaries based on specified column names. @@ -34,3 +37,10 @@ def prepare_table_for_macro(model_objects, column_attrs): ] for obj in model_objects ] + + +def form_validation_failed(request, form): + if request.method == 'POST': + for fieldName, errorMessages in form.errors.items(): + for err in errorMessages: + flash(f"Error in {fieldName}: {err}", 'danger') \ No newline at end of file diff --git a/eveai_app/views/user_forms.py b/eveai_app/views/user_forms.py index 9af68bb..64ef439 100644 --- a/eveai_app/views/user_forms.py +++ b/eveai_app/views/user_forms.py @@ -27,8 +27,11 @@ class TenantForm(FlaskForm): html_included_elements = StringField('HTML Included Elements', validators=[Optional()]) html_excluded_elements = StringField('HTML Excluded Elements', validators=[Optional()]) # Embedding Search variables - es_k = IntegerField('Limit for Searching Embeddings (5)', validators=[NumberRange(min=0)]) + es_k = IntegerField('Limit for Searching Embeddings (5)', + default=5, + validators=[NumberRange(min=0)]) es_similarity_threshold = FloatField('Similarity Threshold for Searching Embeddings (0.5)', + default=0.5, validators=[NumberRange(min=0, max=1)]) submit = SubmitField('Submit') diff --git a/eveai_app/views/user_views.py b/eveai_app/views/user_views.py index 0855b33..9d5f671 100644 --- a/eveai_app/views/user_views.py +++ b/eveai_app/views/user_views.py @@ -12,7 +12,7 @@ from common.extensions import db, kms_client, security from common.utils.security_utils import send_confirmation_email, send_reset_email from .user_forms import TenantForm, CreateUserForm, EditUserForm, TenantDomainForm from common.utils.database import Database -from common.utils.view_assistants import prepare_table_for_macro +from common.utils.view_assistants import prepare_table_for_macro, form_validation_failed from common.utils.key_encryption import generate_api_key from common.utils.nginx_utils import prefixed_url_for @@ -43,6 +43,7 @@ def log_after_request(response): def tenant(): form = TenantForm() if form.validate_on_submit(): + current_app.logger.debug('Creating new tenant') # Handle the required attributes new_tenant = Tenant(name=form.name.data, website=form.website.data, @@ -50,18 +51,23 @@ def tenant(): allowed_languages=form.allowed_languages.data, embedding_model=form.embedding_model.data, llm_model=form.llm_model.data, - licence_start_date=form.license_start_date.data, - lic_end_date=form.license_end_date.data, + license_start_date=form.license_start_date.data, + license_end_date=form.license_end_date.data, allowed_monthly_interactions=form.allowed_monthly_interactions.data) # Handle Embedding Variables - new_tenant.html_tags = form.html_tags.data.split(',') if form.html_tags.data else [], - new_tenant.html_end_tags = form.html_end_tags.data.split(',') if form.html_end_tags.data else [], + new_tenant.html_tags = form.html_tags.data.split(',') if form.html_tags.data else [] + new_tenant.html_end_tags = form.html_end_tags.data.split(',') if form.html_end_tags.data else [] new_tenant.html_included_elements = form.html_included_elements.data.split( - ',') if form.html_included_elements.data else [], + ',') if form.html_included_elements.data else [] new_tenant.html_excluded_elements = form.html_excluded_elements.data.split( ',') if form.html_excluded_elements.data else [] + current_app.logger.debug(f'html_tags: {new_tenant.html_tags},' + f'html_end_tags: {new_tenant.html_end_tags},' + f'html_included_elements: {new_tenant.html_included_elements},' + f'html_excluded_elements: {new_tenant.html_excluded_elements}') + # Handle Timestamps timestamp = dt.now(tz.utc) new_tenant.created_at = timestamp @@ -82,6 +88,8 @@ def tenant(): current_app.logger.info(f"Creating schema for tenant {new_tenant.id}") Database(new_tenant.id).create_tenant_schema() return redirect(prefixed_url_for('basic_bp.index')) + else: + form_validation_failed(request, form) return render_template('user/tenant.html', form=form) @@ -104,7 +112,7 @@ def edit_tenant(tenant_id): if tenant.html_excluded_elements: form.html_excluded_elements.data = ', '.join(tenant.html_excluded_elements) - if request.method == 'POST' and form.validate_on_submit(): + if form.validate_on_submit(): # Populate the tenant with form data form.populate_obj(tenant) # Then handle the special fields manually @@ -121,6 +129,8 @@ def edit_tenant(tenant_id): if session['tenant'].get('id') == tenant_id: session['tenant'] = tenant.to_dict() # return redirect(url_for(f"user/tenant/tenant_id")) + else: + form_validation_failed(request, form) return render_template('user/edit_tenant.html', form=form, tenant_id=tenant_id) @@ -176,6 +186,8 @@ def user(): current_app.logger.error(f'Failed to add user with name {new_user.user_name}. Error: {str(e)}') db.session.rollback() flash(f'Failed to add user. Email or user name already exists.', 'danger') + else: + form_validation_failed(request, form) return render_template('user/user.html', form=form) @@ -186,7 +198,7 @@ def edit_user(user_id): user = User.query.get_or_404(user_id) # This will return a 404 if no user is found form = EditUserForm(obj=user) - if request.method == 'POST' and form.validate_on_submit(): + if form.validate_on_submit(): # Populate the user with form data user.first_name = form.first_name.data user.last_name = form.last_name.data @@ -212,6 +224,8 @@ def edit_user(user_id): return redirect( prefixed_url_for('user_bp.edit_user', user_id=user.id)) # Assuming there's a user profile view to redirect to + else: + form_validation_failed(request, form) form.roles.data = [role.id for role in user.roles] return render_template('user/edit_user.html', form=form, user_id=user_id) @@ -349,6 +363,8 @@ def tenant_domain(): current_app.logger.error(f'Failed to create Tenant Domain {new_tenant_domain.domain}. ' f'for tenant {session["tenant"]["id"]}' f'Error: {str(e)}') + else: + flash('Please fill in all required fields.') return render_template('user/tenant_domain.html', form=form) @@ -375,6 +391,8 @@ def edit_tenant_domain(tenant_domain_id): return redirect( prefixed_url_for('user_bp.view_tenant_domains', tenant_id=session['tenant']['id'])) # Assuming there's a user profile view to redirect to + else: + form_validation_failed(request, form) return render_template('user/edit_tenant_domain.html', form=form, tenant_domain_id=tenant_domain_id) diff --git a/scripts/db_heads.sh b/scripts/db_heads.sh new file mode 100755 index 0000000..2a83c90 --- /dev/null +++ b/scripts/db_heads.sh @@ -0,0 +1,32 @@ +#!/usr/bin/env bash + +# Usage: ./db_heads.sh -d migrations/public + +cd "/Volumes/OWC4M2_1/Dropbox/Josako's Dev/Josako/EveAI/Development/eveAI/" || exit 1 +source "/Volumes/OWC4M2_1/Dropbox/Josako's Dev/Josako/EveAI/Development/eveAI/.venv/bin/activate" + +while getopts d: flag +do + case "${flag}" in + d) directory=${OPTARG};; + *) # Catch-all for unexpected arguments + echo "Invalid option: -$OPTARG" >&2 + echo "Usage: ./migrate.sh -m \"Your migration message\" -d migrations/public" + exit 1 + ;; + esac +done + +# Check if the message and directory are provided +if [ -z "$directory" ]; then + echo "Directory is required." + echo "Usage: ./db_heads.sh -d migrations/public" + exit 1 +fi + +# Set FLASK_APP environment variable +export FLASK_APP=scripts/run_eveai_app.py # Modify if your Flask app is initiated differently +export PYTHONPATH="$PYTHONPATH:/Volumes/OWC4M2_1/Dropbox/Josako's Dev/Josako/EveAI/Development/eveAI/" + +# Run the Flask migration command +flask db heads -d "$directory" \ No newline at end of file