diff --git a/UI/dependences_ui/utils.py b/UI/dependences_ui/utils.py
index 4561038..860155d 100644
--- a/UI/dependences_ui/utils.py
+++ b/UI/dependences_ui/utils.py
@@ -25,13 +25,14 @@ def hash_password(password):
"""Hash password using SHA-256"""
return hashlib.sha256(password.encode()).hexdigest()
+
def associate_user_with_manager(users, user_assignment_manager):
- user_list=users.keys()
+ user_list = users.keys()
print(f"registering--Associating users with manager: {list(user_list)}")
user_assignment_manager.register_active_users(list(user_list))
-def register_user(username, password, confirm_password,user_assignment_manager):
+def register_user(username, password, confirm_password, user_assignment_manager):
"""Register a new user"""
if not username or not password:
return "", "Username and password cannot be empty!", None
@@ -53,7 +54,7 @@ def register_user(username, password, confirm_password,user_assignment_manager):
try:
associate_user_with_manager(users, user_assignment_manager)
except Exception as e:
- print(f"Error associating user with manager: {e}")
+ print(f"Error associating user with manager: {e}")
return "", f"✅ Registration successful! You can now login.", None
@@ -125,3 +126,44 @@ def protected_content(state):
if state.get("logged_in"):
return f"You are logged as {state.get('username')}\n"
return "Please login to access this content."
+
+
+
+def get_user_assessments_done(connection_db, username):
+ """
+ it returns:
+ {
+ "https://example.com/page1": [1, 3, 5],
+ "https://example.com/page2": [2, 4, 6],
+ }
+ """
+
+ cursor = connection_db.cursor()
+ username = json.dumps({"username": username}, ensure_ascii=False)
+ cursor.execute(
+ """
+ SELECT page_url, json_output_data
+ FROM wcag_user_assessments
+ WHERE user = ? AND insert_type = ?
+ ORDER BY page_url
+ """,
+ (username, "wcag_user_llm_alttext_assessments"),
+ )
+
+ rows = cursor.fetchall()
+ assessment_done = {} # dict: {page_url: sorted list of image numbers}
+ for row in rows:
+ page_url = row[0]
+ data = json.loads(row[1])
+ image_numbers = {int(item["Image #"]) for item in data} # set to deduplicate
+ if page_url not in assessment_done:
+ assessment_done[page_url] = image_numbers
+ else:
+ assessment_done[page_url].update(
+ image_numbers
+ ) # merge if url appears multiple times
+
+ # Convert sets to sorted lists
+ assessment_done = {url: sorted(imgs) for url, imgs in assessment_done.items()}
+
+ return assessment_done
diff --git a/UI/user_task_assignment/user_assignment_manager.py b/UI/user_task_assignment/user_assignment_manager.py
index 5fa9c5b..b1a7617 100644
--- a/UI/user_task_assignment/user_assignment_manager.py
+++ b/UI/user_task_assignment/user_assignment_manager.py
@@ -54,6 +54,7 @@ class UserAssignmentManager:
assignments_xlsx_path: str = "alt_text_assignments_output_target_overlap.xlsx",
target_overlap: int = 2,
seed: int = 42,
+
):
"""
Initialize the User Assignment Manager.
@@ -86,8 +87,8 @@ class UserAssignmentManager:
# Initialize database
self._init_database()
- # Load existing assignments from JSON if available
- self._load_existing_assignments()
+ # Load existing assignments to db from JSON if available
+ #self._load_existing_assignments()
def _load_sites_config(self) -> List[SiteConfig]:
"""Load site configuration from JSON."""
@@ -160,7 +161,7 @@ class UserAssignmentManager:
conn.commit()
conn.close()
- def _load_existing_assignments(self, active_user_names: Optional[List[str]] = None):
+ def _load_existing_assignments(self, active_user_names: List[str] = []):
"""Load existing assignments from JSON file into database if not already there."""
if not self.assignments_json_path.exists():
return
@@ -172,17 +173,15 @@ class UserAssignmentManager:
conn = sqlite3.connect(self.db_path)
cursor = conn.cursor()
+ # nb: every service restart and user registration will trigger this (ONCONFLICT ensures no duplicates)
for user_id, sites_dict in assignments.items():
- for site_url, image_indices in sites_dict.items():
- # print(f"[DB] Loading assignment for user {user_id}, site {site_url}, "
- # f"{image_indices} images")
- try:
- '''
- cursor.execute("""
- INSERT OR IGNORE INTO user_assignments
- (user_id, site_url, image_indices)
- VALUES (?, ?, ?)
- """, (user_id, site_url, json.dumps(image_indices)))'''
+ try:
+ for site_url, image_indices in sites_dict.items():
+
+ print(
+ f"[DB] Loading assignment for user {user_id}, site {site_url}, "
+ f"{image_indices} images"
+ )
cursor.execute(
"""
@@ -195,28 +194,26 @@ class UserAssignmentManager:
(user_id, site_url, json.dumps(image_indices)),
)
- cursor.execute( # also update user_info table with user_name if active_user_names is provided and user_id starts with "user"
- """
- INSERT INTO user_info (user_id, user_name)
- VALUES (?, ?)
- ON CONFLICT(user_id) DO UPDATE SET
- user_name = excluded.user_name
- """,
+ cursor.execute( # also update user_info table with user_name if active_user_names is provided and user_id starts with "user"
+ """
+ INSERT INTO user_info (user_id, user_name)
+ VALUES (?, ?)
+ ON CONFLICT(user_id) DO UPDATE SET
+ user_name = excluded.user_name
+ """,
+ (
+ user_id,
(
- user_id,
- (
- active_user_names[int(user_id[4:]) - 1]
- if active_user_names and user_id.startswith("user")
- else None
- ),
+ active_user_names[int(user_id[4:]) - 1]
+ if active_user_names and user_id.startswith("user")
+ else None
),
- )
+ ),
+ )
- except sqlite3.IntegrityError:
- print(
- f"[DB] Error. Skipping existing assignment for user {user_id}, site {site_url}"
- )
- pass
+ except sqlite3.IntegrityError:
+ print(f"[DB] Error. Skipping existing assignment for user {user_id}")
+ pass
conn.commit()
conn.close()
@@ -243,7 +240,7 @@ class UserAssignmentManager:
if from_user_name:
print(f"[DB] Looking up user_id for user_name: {user_id}")
-
+
cursor.execute(
"""
SELECT user_id
diff --git a/UI/wcag_validator_ui.py b/UI/wcag_validator_ui.py
index 159d4b6..9f1c999 100644
--- a/UI/wcag_validator_ui.py
+++ b/UI/wcag_validator_ui.py
@@ -18,7 +18,9 @@ from dependences.utils import (
db_persistence_startup,
db_persistence_insert,
return_from_env_valid,
+
)
+
from dependences_ui.utils import *
import logging
import time
@@ -31,18 +33,37 @@ import sqlite3
from user_task_assignment.user_assignment_manager import UserAssignmentManager
+from dependences_ui.utils import load_users,get_user_assessments_done
+users=load_users()
+user_list=list(users.keys())
+print(f"Loaded users from simple JSON: {len(user_list)}")
user_assignment_manager = UserAssignmentManager(
db_path="persistence/wcag_validator_ui.db",
config_json_path="user_task_assignment/sites_config.json",
assignments_json_path="user_task_assignment/alt_text_assignments_output_target_overlap.json",
- assignments_xlsx_path="user_task_assignment/alt_text_assignments_output_target_overlap.xlsx"
+ assignments_xlsx_path="user_task_assignment/alt_text_assignments_output_target_overlap.xlsx",
+
)
# Get current managed users
-managed_users = user_assignment_manager.get_all_user_ids()
-print(f"Currently managed users from db: {managed_users}")
-print(f"Total managed users from db: {user_assignment_manager.get_managed_user_count()}\n")
+managed_users_number = user_assignment_manager.get_managed_user_count()
+print(f"Currently managed users from db: {managed_users_number}")
+if managed_users_number !=len(user_list):# rigenenerate files only if some user numbers disalignmnets. Avoid only updates on new user registration process
+ print(f"Warning: Number of users in db ({managed_users_number}) does not match number of users loaded from JSON ({len(user_list)}). Re-init user assignments files.")
+ user_assignment_manager.register_active_users(user_list)#on startup register users loaded from JSON into the manager (creating also assignments .json amd .xml files)
+ # Get current managed users after regsitration alignment
+ managed_users_number = user_assignment_manager.get_managed_user_count()
+ print(f"Currently managed users from db after alignment: {managed_users_number}")
+
+# Get current managed users after regsitration alignment
+
+print(f"Total managed users from db: {managed_users_number}\n")
+if managed_users_number !=len(user_list):
+ print(f"Warning: Number of users in db ({managed_users_number}) does not match number of users loaded from JSON ({len(user_list)}). Check user assignment manager initialization.")
+ exit(1)
+
+
user_assignment_stats = user_assignment_manager.get_statistics()
print(f"Current assignment stats:{user_assignment_stats} \n")
@@ -52,9 +73,81 @@ print(f"Current assignment stats:{user_assignment_stats} \n")
WCAG_VALIDATOR_RESTSERVER_HEADERS = [("Content-Type", "application/json")]
-def display_user_assignment(user_state):
+def maybe_close_modal(process_dataframe_output_state):
+ print("Checking if modal can be closed based on:",type(process_dataframe_output_state), process_dataframe_output_state)
+ if not process_dataframe_output_state:
+ print("Modal cannot be closed.")
+ return Modal(visible=True) # keep it open
+ return Modal(visible=False) # close it
+
+def maybe_open_modal(make_alttext_llm_assessment_api_call_output_state):
+ print("Checking if modal can be opened based on:",type(make_alttext_llm_assessment_api_call_output_state), make_alttext_llm_assessment_api_call_output_state)
+ if not make_alttext_llm_assessment_api_call_output_state:
+ print("Modal cannot be opened.")
+ return Modal(visible=False)
+ return Modal(visible=True)
+
+def render_user_assessmnet_status_table(df):
+ if df is None or df.empty:
+ return "
No assignments found.
"
+
+ total_work_to_be_done=[]
+ rows = ""
+ for _, row in df.iterrows():
+ url = row["Website URL"]
+ assigned = row["Assigned Image Number"]
+ work_done = row["Work Done on Image Number"]
+ work_to_be_done = [img for img in assigned if img not in work_done]
+ total_work_to_be_done+=work_to_be_done
+
+ rows += f"""
+