diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/models/user.py b/spiffworkflow-backend/src/spiffworkflow_backend/models/user.py index 0fc0d89d..cf449ac8 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/models/user.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/models/user.py @@ -27,12 +27,12 @@ class UserModel(SpiffworkflowBaseDBModel): id: int = db.Column(db.Integer, primary_key=True) username: str = db.Column(db.String(255), nullable=False, unique=True) - email = db.Column(db.String(255), index=True) + email: str = db.Column(db.String(255), index=True) service = db.Column(db.String(255), nullable=False, unique=False, index=True) # not 'openid' -- google, aws service_id = db.Column(db.String(255), nullable=False, unique=False, index=True) - display_name = db.Column(db.String(255)) + display_name: str = db.Column(db.String(255)) tenant_specific_field_1: str | None = db.Column(db.String(255)) tenant_specific_field_2: str | None = db.Column(db.String(255)) tenant_specific_field_3: str | None = db.Column(db.String(255)) diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/routes/onboarding_controller.py b/spiffworkflow-backend/src/spiffworkflow_backend/routes/onboarding_controller.py index 5ee3bff4..05fdad2e 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/routes/onboarding_controller.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/routes/onboarding_controller.py @@ -4,7 +4,10 @@ from contextlib import suppress from flask import make_response from flask.wrappers import Response +from spiffworkflow_backend import db +from spiffworkflow_backend.models.task import TaskModel from spiffworkflow_backend.routes.process_instances_controller import _process_instance_start +from spiffworkflow_backend.services.jinja_service import JinjaService def get_onboarding() -> Response: @@ -21,7 +24,14 @@ def get_onboarding() -> Response: result = { "type": "user_input_required", "process_instance_id": process_instance.id, - "task_id": process_instance.active_human_tasks[0].task_id, } + task = processor.next_task() + db.session.flush() + if task: + task_model: TaskModel | None = TaskModel.query.filter_by(guid=str(task.id), + process_instance_id=process_instance.id).first() + result['task_id'] = task_model.guid + result['instructions'] = JinjaService.render_instructions_for_end_user(task_model) + return make_response(result, 200) diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/scripts/times_executed_by_user.py b/spiffworkflow-backend/src/spiffworkflow_backend/scripts/times_executed_by_user.py new file mode 100644 index 00000000..616c60d9 --- /dev/null +++ b/spiffworkflow-backend/src/spiffworkflow_backend/scripts/times_executed_by_user.py @@ -0,0 +1,22 @@ +from typing import Any + +from spiffworkflow_backend.models.script_attributes_context import ScriptAttributesContext +from spiffworkflow_backend.scripts.script import Script +from spiffworkflow_backend.services.process_instance_service import ProcessInstanceService + + +class TimesExecutedByUser(Script): + @staticmethod + def requires_privileged_permissions() -> bool: + """We have deemed this function safe to run without elevated permissions.""" + return False + + def get_description(self) -> str: + return """Returns boolean to indicate if the user has started an instance of the current process model.""" + + def run(self, script_attributes_context: ScriptAttributesContext, *_args: Any, **kwargs: Any) -> Any: + process_model_identifer = script_attributes_context.process_model_identifier + if process_model_identifer is not None: + return ProcessInstanceService.times_executed_by_user(process_model_identifer) + else: + return False diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_service.py b/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_service.py index 97295c88..2834ddc9 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_service.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_service.py @@ -57,6 +57,18 @@ class ProcessInstanceService: ) return started_instance is not None + def times_executed_by_user(process_model_identifier: str) -> bool: + total = ( + db.session.query(ProcessInstanceModel) + .filter( + ProcessInstanceModel.process_model_identifier == process_model_identifier + ) + .count() + ) + return total + + + @staticmethod def next_start_event_configuration(process_instance_model: ProcessInstanceModel) -> StartConfiguration: try: diff --git a/spiffworkflow-backend/tests/spiffworkflow_backend/scripts/test_get_current_user.py b/spiffworkflow-backend/tests/spiffworkflow_backend/scripts/test_get_current_user.py index afcd7437..bcfb382f 100644 --- a/spiffworkflow-backend/tests/spiffworkflow_backend/scripts/test_get_current_user.py +++ b/spiffworkflow-backend/tests/spiffworkflow_backend/scripts/test_get_current_user.py @@ -17,6 +17,7 @@ class TestGetCurrentUser(BaseTest): ) -> None: testuser1 = self.find_or_create_user("testuser1") testuser1.tenant_specific_field_1 = "456" + testuser1.display_name = "Test User NUMBER ONE!" db.session.add(testuser1) db.session.commit() @@ -35,4 +36,5 @@ class TestGetCurrentUser(BaseTest): ) assert result["username"] == "testuser1" assert result["tenant_specific_field_1"] == "456" + assert result["display_name"] == "Test User NUMBER ONE!" json.dumps(result) diff --git a/spiffworkflow-frontend/src/index.scss b/spiffworkflow-frontend/src/index.scss index 0235b66a..0497d5e1 100644 --- a/spiffworkflow-frontend/src/index.scss +++ b/spiffworkflow-frontend/src/index.scss @@ -91,3 +91,7 @@ div.cds--tag svg { vertical-align: middle; display: inline-block; } + +div.onboarding { + margin-bottom: 2rem; +} \ No newline at end of file diff --git a/spiffworkflow-frontend/src/interfaces.ts b/spiffworkflow-frontend/src/interfaces.ts index 6b70541b..6a1763df 100644 --- a/spiffworkflow-frontend/src/interfaces.ts +++ b/spiffworkflow-frontend/src/interfaces.ts @@ -15,6 +15,7 @@ export interface Onboarding { value?: string; process_instance_id?: string; task_id?: string; + instructions: string; } export interface ProcessData { diff --git a/spiffworkflow-frontend/src/routes/HomePageRoutes.tsx b/spiffworkflow-frontend/src/routes/HomePageRoutes.tsx index 83ee83ef..f0acd92e 100644 --- a/spiffworkflow-frontend/src/routes/HomePageRoutes.tsx +++ b/spiffworkflow-frontend/src/routes/HomePageRoutes.tsx @@ -49,9 +49,9 @@ export default function HomePageRoutes() { return (
+ {renderTabs()} - } /> } /> } /> } /> diff --git a/spiffworkflow-frontend/src/routes/OnboardingView.tsx b/spiffworkflow-frontend/src/routes/OnboardingView.tsx index a25561ff..df9bd490 100644 --- a/spiffworkflow-frontend/src/routes/OnboardingView.tsx +++ b/spiffworkflow-frontend/src/routes/OnboardingView.tsx @@ -1,5 +1,6 @@ -import { useEffect, useState } from 'react'; +import React, { useEffect, useState } from 'react'; import { useNavigate } from 'react-router-dom'; +import MDEditor from '@uiw/react-md-editor'; import HttpService from '../services/HttpService'; import InProgressInstances from './InProgressInstances'; import { Onboarding } from '../interfaces'; @@ -18,11 +19,23 @@ export default function OnboardingView() { }, [setOnboarding]); const onboardingElement = () => { - if (onboarding) { + if (onboarding && onboarding.instructions.length > 0) { + return ( + + ); + /* if (onboarding.type === 'default_view') { if (onboarding.value === 'my_tasks') { return ; } + } else if ( + onboarding.type === 'user_input_required' + ) { + console.log("onboarding"); } else if ( onboarding.type === 'user_input_required' && onboarding.process_instance_id && @@ -32,9 +45,9 @@ export default function OnboardingView() { `/tasks/${onboarding.process_instance_id}/${onboarding.task_id}` ); } + */ } - - return ; + return null; }; return onboardingElement();