Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion cypress/e2e/preferences-segmentation-ai.cy.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ describe('Preferences Modal - Segmentation AI Tab', () => {

// Verify modal is open
cy.get('[data-testid="preferences-modal"]').should('be.visible');
cy.contains('h2', 'Preferences').should('be.visible');
cy.get('[data-testid="preferences-modal"]').contains('Preferences').should('be.visible');

// Close preferences and verify
cy.closePreferences();
Expand Down
37 changes: 26 additions & 11 deletions cypress/support/commands.js
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,15 @@ Cypress.Commands.add('login', (username = 'admin', password = '123') => {
cy.get('#register-password').should('be.visible').should('not.be.disabled').clear().type(password);
cy.get('#register-password-again').should('be.visible').should('not.be.disabled').clear().type(password);

// Click register and wait for page reload
cy.get('.dialogue').contains('button', 'Register').click();
// Fill email if the field exists (required by themed LoginForm)
cy.get('body').then($body => {
if ($body.find('#register-email').length > 0) {
cy.get('#register-email').should('be.visible').clear().type(`${username}@test.com`);
}
});

// Click register button (works with both legacy .dialogue and themed LoginForm)
cy.contains('button', 'Register').click();

// Wait for the page to reload (LoginForm calls window.location.reload())
cy.url().should('include', '/segmentation');
Expand All @@ -99,18 +106,17 @@ Cypress.Commands.add('login', (username = 'admin', password = '123') => {
cy.get('#login-username').should('be.visible').should('not.be.disabled').clear().type(username);
cy.get('#login-password').should('be.visible').should('not.be.disabled').clear().type(password);

// Click login and wait for page reload
cy.get('.dialogue').contains('button', 'Login').click();
// Click login button (works with both legacy .dialogue and themed LoginForm)
cy.contains('button', 'Login').click();

// Wait for the page to reload (LoginForm calls window.location.reload())
cy.wait(TIMEOUTS.PAGE_LOAD * 3); // Extra time for reload

// Debug: Check if we're still seeing a login dialog
cy.get('body').then($body => {
const $dialogue = $body.find('.dialogue');
if ($dialogue.length > 0 && $dialogue.css('display') !== 'none') {
cy.log('⚠️ Login dialog still visible after reload - login may have failed');
// Take a screenshot for debugging
const $loginForm = $body.find('#login-username:visible');
if ($loginForm.length > 0) {
cy.log('⚠️ Login form still visible after reload - login may have failed');
cy.screenshot('login-failed-dialog-still-visible');
}
});
Expand Down Expand Up @@ -147,12 +153,21 @@ Cypress.Commands.add('login', (username = 'admin', password = '123') => {
*/
Cypress.Commands.add('closeHelpDialogue', () => {
cy.get('body').then($body => {
// Check for themed HelpModal (data-testid="help-modal")
const $helpModal = $body.find('[data-testid="help-modal"]');
if ($helpModal.length > 0 && $helpModal.is(':visible')) {
cy.log('Closing themed help modal...');
cy.get('[data-testid="close-help-button"]').click({ force: true });
cy.wait(500);
cy.get('[data-testid="help-modal"]').should('not.exist');
return;
}
// Fallback: check for legacy #dialogue
const $dialogue = $body.find('#dialogue');
if ($dialogue.length > 0 && $dialogue.css('display') !== 'none' && $dialogue.is(':visible')) {
cy.log('Closing dialogue...');
// Try to find and click the close button
cy.log('Closing legacy dialogue...');
cy.get('#dialogue-close').click({ force: true });
cy.wait(500); // Wait for dialogue to close
cy.wait(500);
cy.get('#dialogue').should('not.be.visible');
}
});
Expand Down
14 changes: 4 additions & 10 deletions iris/__init__.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,12 @@
import json
import os
import shutil
import sys
import webbrowser
from getpass import getpass
from os.path import basename, dirname, exists, isabs, join
from os.path import join
from pathlib import Path
from typing import Union

import flask
import numpy as np
import yaml

__version__ = "1.0.0"

Expand Down Expand Up @@ -97,8 +93,8 @@ def handle_launch_command(folder_name: str) -> Path:

try:
dconfig = json.loads(config_file.read_text(encoding="utf-8"))
except json.JSONDecodeError:
raise RuntimeError(f"Error: Invalid JSON in {config_file}")
except json.JSONDecodeError as err:
raise RuntimeError(f"Error: Invalid JSON in {config_file}") from err
dconfig["name"] = folder_name
with config_file.open("w", encoding="utf-8") as fp:
json.dump(dconfig, fp, indent=4)
Expand Down Expand Up @@ -271,8 +267,6 @@ def register_extensions(app):
register_segmentation_blueprints(app)
from iris.admin import register_admin_blueprints
register_admin_blueprints(app)
from iris.help import help_app
app.register_blueprint(help_app, url_prefix="/help")
from iris.user import register_user_api, user_app
app.register_blueprint(user_app, url_prefix="/user")
register_user_api(app)
Expand All @@ -285,7 +279,7 @@ def register_extensions(app):
# Decide whether to parse CLI args. If help is requested or the first token
# Module-level initialization for when IRIS is imported (not run as CLI)
# This is used by tests and when importing iris as a library
from iris.models import Action, User
from iris.models import Action, User # noqa: E402, F401

# Create default app for imports/tests
_default_args = {
Expand Down
20 changes: 0 additions & 20 deletions iris/help/__init__.py

This file was deleted.

Empty file removed iris/help/templates/help.html
Empty file.
2 changes: 0 additions & 2 deletions iris/main/templates/base.html
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
'segmentation': {{url_for('segmentation_spa.segmentation_spa')|tojson}},
'user': {{url_for('user.index')|tojson}},
'admin': {{url_for('admin_spa.admin_spa')|tojson}},
'help': {{url_for('help.index')|tojson}},
},
'config': null,
// 'show_dialogue_before_next_image': MIGRATED to React store (segmentationStore)
Expand All @@ -27,7 +26,6 @@
if (window.setApiUrlsInStore && vars.url) {
try {
window.setApiUrlsInStore(vars.url);
console.log('[IRIS Migration] ✅ API URLs initialized in React store');
} catch (error) {
console.error('[IRIS Migration] ❌ Failed to initialize API URLs in React store:', error);
}
Expand Down
Empty file removed iris/main/templates/index.html
Empty file.
6 changes: 3 additions & 3 deletions iris/project.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ def load_from(self, filename):
elif filename.endswith('yaml'):
self.config = yaml.safe_load(stream)
except Exception as error:
raise Exception('[CONFIG] Error in config file: '+ str(error))
raise Exception('[CONFIG] Error in config file: '+ str(error)) from error

# Load default config:
with open(join(dirname(__file__), "default_config.json")) as stream:
Expand Down Expand Up @@ -194,10 +194,10 @@ def _init_paths_and_files(self, filename):
regex_images.match(image_path).groups()[0]
for image_path in images
])
except Exception:
except Exception as err:
raise Exception(
f'[ERROR] Could not extract id\nfrom path"{image_paths}"\nwith regex "{regex_images}"!'
)
) from err


def make_absolute(self, path):
Expand Down
18 changes: 10 additions & 8 deletions iris/segmentation/__init__.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,16 @@
import json
import os
import time
from datetime import datetime, timedelta
from datetime import datetime
from glob import glob
from os.path import basename, dirname, exists, join
from pprint import pprint
from os.path import basename, dirname, join

import flask
import lightgbm as lgb
import numpy as np
import yaml
from scipy.ndimage import convolve, maximum_filter, minimum_filter
from scipy.ndimage import convolve
from skimage.filters import sobel
from skimage.io import imread, imsave
from skimage.io import imsave
from skimage.segmentation import felzenszwalb
from sklearn.metrics import accuracy_score, f1_score, jaccard_score
from sklearn.model_selection import train_test_split
Expand All @@ -28,8 +26,8 @@
)

# Import SPA and API blueprints
from .api import api_bp
from .spa import spa_bp
from .api import api_bp # noqa: E402
from .spa import spa_bp # noqa: E402


def register_segmentation_blueprints(app):
Expand Down Expand Up @@ -247,6 +245,10 @@ def load_mask(image_id):
data.astype(np.uint8).tobytes()
)
response.headers.set('Content-Type', 'application/octet-stream')
# Prevent browser caching to ensure fresh mask data on each load
response.headers.set('Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0')
response.headers.set('Pragma', 'no-cache')
response.headers.set('Expires', '0')
return response
except Exception:
return flask.make_response("No user mask available!", 404)
Expand Down
2 changes: 1 addition & 1 deletion iris/segmentation/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ def get_config():
"""Get project configuration as JSON."""
# Get the full project config
config = project.config

return flask.jsonify(config)


Expand Down
Loading
Loading