diff --git a/DigitalLearningSolutions.Data.Migrations/202501280929_CreateCompetencyAssessmentTables.cs b/DigitalLearningSolutions.Data.Migrations/202501280929_CreateCompetencyAssessmentTables.cs
new file mode 100644
index 0000000000..3ad9669e2c
--- /dev/null
+++ b/DigitalLearningSolutions.Data.Migrations/202501280929_CreateCompetencyAssessmentTables.cs
@@ -0,0 +1,57 @@
+namespace DigitalLearningSolutions.Data.Migrations
+{
+ using FluentMigrator;
+ using FluentMigrator.SqlServer;
+ [Migration(202501280929)]
+ public class CreateCompetencyAssessmentTables : Migration
+ {
+ public override void Up()
+ {
+ Create.Table("SelfAssessmentFrameworks")
+ .WithColumn("ID").AsInt32().NotNullable().PrimaryKey().Identity()
+ .WithColumn("SelfAssessmentId").AsInt32().NotNullable().ForeignKey("SelfAssessments", "ID")
+ .WithColumn("FrameworkId").AsInt32().NotNullable().ForeignKey("Frameworks", "ID")
+ .WithColumn("CreatedDate").AsDateTime().NotNullable().WithDefault(SystemMethods.CurrentDateTime)
+ .WithColumn("CreatedByAdminId").AsInt32().NotNullable().ForeignKey("AdminAccounts", "ID")
+ .WithColumn("RemovedDate").AsDateTime().Nullable()
+ .WithColumn("RemovedByAdminId").AsInt32().Nullable().ForeignKey("AdminAccounts", "ID")
+ .WithColumn("AmendedDate").AsDateTime().Nullable()
+ .WithColumn("AmendedByAdminId").AsInt32().Nullable().ForeignKey("AdminAccounts", "ID");
+ Create.Table("SelfAssessmentTaskStatus")
+ .WithColumn("ID").AsInt32().NotNullable().PrimaryKey().Identity()
+ .WithColumn("SelfAssessmentId").AsInt32().NotNullable().ForeignKey("SelfAssessments", "ID").Unique()
+ .WithColumn("IntroductoryTextTaskStatus").AsBoolean().Nullable()
+ .WithColumn("BrandingTaskStatus").AsBoolean().Nullable()
+ .WithColumn("VocabularyTaskStatus").AsBoolean().Nullable()
+ .WithColumn("WorkingGroupTaskStatus").AsBoolean().Nullable()
+ .WithColumn("NationalRoleProfileTaskStatus").AsBoolean().Nullable()
+ .WithColumn("FrameworkLinksTaskStatus").AsBoolean().Nullable()
+ .WithColumn("SelectCompetenciesTaskStatus").AsBoolean().Nullable()
+ .WithColumn("OptionalCompetenciesTaskStatus").AsBoolean().Nullable()
+ .WithColumn("RoleRequirementsTaskStatus").AsBoolean().Nullable()
+ .WithColumn("SupervisorRolesTaskStatus").AsBoolean().Nullable()
+ .WithColumn("SelfAssessmentOptionsTaskStatus").AsBoolean().Nullable()
+ .WithColumn("ReviewTaskStatus").AsBoolean().Nullable();
+ Alter.Table("SelfAssessments").AlterColumn("Description").AsString(int.MaxValue).Nullable();
+ Execute.Sql($@"INSERT INTO SelfAssessmentFrameworks (SelfAssessmentId, FrameworkId, CreatedByAdminId)
+ SELECT sa.ID, fc.FrameworkID, sa.CreatedByAdminID
+ FROM SelfAssessments AS sa INNER JOIN
+ SelfAssessmentStructure AS sas ON sa.ID = sas.SelfAssessmentID INNER JOIN
+ FrameworkCompetencies AS fc ON sas.CompetencyID = fc.CompetencyID
+ GROUP BY sa.ID, fc.FrameworkID, sa.CreatedByAdminID
+ ");
+ Execute.Sql($@"INSERT INTO SelfAssessmentTaskStatus (SelfAssessmentId, IntroductoryTextTaskStatus, BrandingTaskStatus, VocabularyTaskStatus, WorkingGroupTaskStatus, NationalRoleProfileTaskStatus, FrameworkLinksTaskStatus, SelectCompetenciesTaskStatus, OptionalCompetenciesTaskStatus, RoleRequirementsTaskStatus, SupervisorRolesTaskStatus, SelfAssessmentOptionsTaskStatus)
+ SELECT ID, 1,1,1,1,1,1,1,1,1,1,1
+ FROM SelfAssessments AS sa
+ ");
+ }
+
+ public override void Down()
+ {
+ Delete.Table("SelfAssessmentFrameworks");
+ Delete.Table("SelfAssessmentTaskStatus");
+ Alter.Table("SelfAssessments").AlterColumn("Description").AsString(int.MaxValue).NotNullable();
+ }
+
+ }
+}
diff --git a/DigitalLearningSolutions.Data.Migrations/202504241517_AddFieldIsPrimaryToSelfAssessmentFrameworksTable.cs b/DigitalLearningSolutions.Data.Migrations/202504241517_AddFieldIsPrimaryToSelfAssessmentFrameworksTable.cs
new file mode 100644
index 0000000000..fe1e096135
--- /dev/null
+++ b/DigitalLearningSolutions.Data.Migrations/202504241517_AddFieldIsPrimaryToSelfAssessmentFrameworksTable.cs
@@ -0,0 +1,12 @@
+namespace DigitalLearningSolutions.Data.Migrations
+{
+ using FluentMigrator;
+ [Migration(202504241517)]
+ public class AddFieldIsPrimaryToSelfAssessmentFrameworksTable : AutoReversingMigration
+ {
+ public override void Up()
+ {
+ Alter.Table("SelfAssessmentFrameworks").AddColumn("IsPrimary").AsBoolean().NotNullable().WithDefaultValue(1);
+ }
+ }
+}
diff --git a/DigitalLearningSolutions.Data.Migrations/202507111455_UpdatePrivacyNoticeAcceptableUse.cs b/DigitalLearningSolutions.Data.Migrations/202507111455_UpdatePrivacyNoticeAcceptableUse.cs
new file mode 100644
index 0000000000..b9dc84ab7e
--- /dev/null
+++ b/DigitalLearningSolutions.Data.Migrations/202507111455_UpdatePrivacyNoticeAcceptableUse.cs
@@ -0,0 +1,430 @@
+using FluentMigrator;
+namespace DigitalLearningSolutions.Data.Migrations
+{
+ [Migration(202507111455)]
+ public class UpdatePrivacyNoticeAcceptableUse : Migration
+ {
+ public override void Up()
+ {
+ var PrivacyPolicyNew = @"
PRIVACY NOTICE
This page explains our privacy policy and how we will use and protect any information about you that you give to us or that we collate when you visit this website, or undertake employment with NHS England (NHSE or we/us/our), or participate in any NHSE sponsored training, education and development including via any of our training platform websites (Training).
This privacy notice is intended to provide transparency regarding what personal data NHSE may hold about you, how it will be processed and stored, how long it will be retained and who may have access to your data.
Personal data is any information relating to an identified or identifiable living person (known as the data subject). An identifiable person is one who can be identified, directly or indirectly, in particular by reference to an identifier such as a name, an identification number or factors specific to the physical, genetic or mental identity of that person, for example.
1 OUR ROLE IN THE NHS
We are here to improve the quality of healthcare for the people and patients of England through education, training and lifelong development of staff and appropriate planning of the workforce required to deliver healthcare services in England.
We aim to enable high quality, effective, compassionate care and to identify the right people with the right skills and the right values. All the information we collect is to support these objectives.
2 IMPORTANT INFORMATION
NHSE is the data controller in respect of any personal data it holds concerning trainees in Training, individuals employed by NHSE and individuals accessing NHSE’s website.
We have appointed a data protection officer (DPO) who is responsible for overseeing questions in relation to this privacy policy. If you have any questions about this privacy policy or our privacy practices, want to know more about how your information will be used, or make a request to exercise your legal rights, please contact our DPO in the following ways:
Name: Andrew Todd
Email address: gdpr@hee.nhs.uk
Postal address: NHS England of Skipton House, 80 London Road, London SE1 6LH
3 WHAT THIS PRIVACY STATEMENT COVERS
This privacy statement only covers the processing of personal data by NHSE that NHSE has obtained from data subjects accessing any of NHSE’s websites and from its provision of services and exercise of functions. It does not cover the processing of data by any sites that can be linked to or from NHSE’s websites, so you should always be aware when you are moving to another site and read the privacy statement on that website.
When providing NHSE with any of your personal data for the first time, for example, when you take up an appointment with NHSE or when you register for any Training, you will be asked to confirm that you have read and accepted the terms of this privacy statement. A copy of your acknowledgement will be retained for reference.
If our privacy policy changes in any way, we will place an updated version on this page. Regularly reviewing the page ensures you are always aware of what information we collect, how we use it and under what circumstances, if any, we will share it with other parties.
4 WHY NHSE COLLECTS YOUR PERSONAL DATA
Personal data may be collected from you via the recruitment process, when you register and/or create an account for any Training, during your Annual Review of Competence Progression or via NHSE’s appraisal process. Personal data may also be obtained from Local Education Providers or employing organisations in connection with the functions of NHSE.
Your personal data is collected and processed for the purposes of and in connection with the functions that NHSE performs with regard to Training and planning. The collection and processing of such data is necessary for the purposes of those functions.
A full copy of our notification to the Information Commissioner’s Office (ICO) (the UK regulator for data protection issues), can be found on the ICO website here: www.ico.org.uk by searching NHSE’s ICO registration number, which is Z2950066.
In connection with Training, NHSE collects and uses your personal information for the following purposes:
- to manage your Training and programme, including allowing you to access your own learning history;
- to quality assure Training programmes and ensure that standards are maintained, including gathering feedback or input on the service, content, or layout of the Training and customising the content and/or layout of the Training;
- to identify workforce planning targets;
- to maintain patient safety through the management of performance concerns;
- to comply with legal and regulatory responsibilities including revalidation;
- to contact you about Training updates, opportunities, events, surveys and information that may be of interest to you;
- transferring your Training activity records for programmes to other organisations involved in medical training in the healthcare sector. These organisations include professional bodies that you may be a member of, such as a medical royal college or foundation school; or employing organisations, such as trusts;
- making your Training activity records visible to specific named individuals, such as tutors, to allow tutors to view their trainees’ activity. We would seek your explicit consent before authorising anyone else to view your records;
- providing anonymous, summarised data to partner organisations, such as professional bodies; or local organisations, such as strategic health authorities or trusts;
- for NHSE internal review;
- to provide HR related support services and Training to you, for clinical professional learner recruitment;
- to promote our services;
- to monitor our own accounts and records;
- to monitor our work, to report on progress made; and
- to let us fulfil our statutory obligations and statutory returns as set by the Department of Health and the law (for example complying with NHSE’s legal obligations and regulatory responsibilities under employment law).
Further information about our use of your personal data in connection with Training can be found in ’A Reference Guide for Postgraduate Foundation and Specialty Training in the UK’, published by the Conference of Postgraduate Medical Deans of the United Kingdom and known as the ‘Gold Guide’, which can be found here: https://www.copmed.org.uk/gold-guide.
5 TYPES OF PERSONAL DATA COLLECTED BY NHSE
The personal data that NHSE collects when you register for Training enables the creation of an accurate user profile/account, which is necessary for reporting purposes and to offer Training that is relevant to your needs.
The personal data that is stored by NHSE is limited to information relating to your work, such as your job role, place of work, and membership number for a professional body (e.g. your General Medical Council number). NHSE will never ask for your home address or any other domestic information.
When accessing Training, you will be asked to set up some security questions, which may contain personal information. These questions enable you to log in if you forget your password and will never be used for any other purpose. The answers that you submit when setting up these security questions are encrypted in the database so no one can view what has been entered, not even NHSE administrators.
NHSE also store a record of some Training activity, including upload and download of Training content, posts on forums or other communication media, and all enquires to the service desks that support the Training.
If you do not provide personal data that we need from you when requested, we may not be able to provide services (such as Training) to you. In this case, we may have to cancel such service, but we will notify you at the time if this happens.
6 COOKIES
When you access NHSE’s website and Training, we want to make them easy, useful and reliable. This sometimes involves placing small amounts of limited information on your device (such as your computer or mobile phone). These small files are known as cookies, and we ask you to agree to their usage in accordance with ICO guidance.
These cookies are used to improve the services (including the Training) we provide you through, for example:
- enabling a service to recognise your device, so you do not have to give the same information several times during one task (e.g. we use a cookie to remember your username if you check the ’Remember Me’ box on a log in page);
- recognising that you may already have given a username and password, so you do not need to do it for every web page requested;
- measuring how many people are using services, so they can be made easier to use and there is enough capacity to ensure they are fast; and
- analysing anonymised data to help us understand how people interact with services so we can make them better.
We use a series of cookies to monitor website speed and usage, as well as to ensure that any preferences you have selected previously are the same when you return to our website. Please visit our cookie policies page to understand the cookies that we use: https://www.dls.nhs.uk/v2/CookieConsent/CookiePolicy
Most cookies applied when accessing Training are used to keep track of your input when filling in online forms, known as session-ID cookies, which are exempt from needing consent as they are deemed essential for using the website or Training they apply to. Some cookies, like those used to measure how you use the Training, are not needed for our website to work. These cookies can help us improve the Training, but we’ll only use them if you say it’s OK. We’ll use a cookie to save your settings.
On a number of pages on our website or Training, we use ’plug-ins’ or embedded media. For example, we might embed YouTube videos. Where we have used this type of content, the suppliers of these services may also set cookies on your device when you visit their pages. These are known as ’third-party’ cookies. To opt-out of third-parties collecting any data regarding your interaction on our website, please refer to their websites for further information.
We will not use cookies to collect personal data about you. However, if you wish to restrict or block the cookies which are set by our websites or Training, or indeed any other website, you can do this through your browser settings. The ’Help’ function within your browser should tell you how. Alternatively, you may wish to visit www.aboutcookies.org which contains comprehensive information on how to do this on a wide variety of browsers. You will also find details on how to delete cookies from your machine as well as more general information about cookies. Please be aware that restricting cookies may impact on the functionality of our website.
7 LEGAL BASIS FOR PROCESSING
The retained EU law version of the General Data Protection Regulation ((EU) 2016/679) (UK GDPR) requires that data controllers and organisations that process personal data demonstrate compliance with its provisions. This involves publishing our basis for lawful processing of personal data.
As personal data is processed for the purposes of NHSE’s statutory functions, NHSE’s legal bases for the processing of personal data as listed in Article 6 of the UK GDPR are as follows:
- 6(1)(a) – Consent of the data subject
- 6(1)(b) – Processing is necessary for the performance of a contract to which the data subject is party or in order to take steps at the request of the data subject prior to entering into a contract
- 6(1)(c) – Processing is necessary for compliance with a legal obligation
- 6(1)(e) – Processing is necessary for the performance of a task carried out in the public interest or in the exercise of official authority vested in the controller
Where NHSE processes special categories of personal data, its additional legal bases for processing such data as listed in Article 9 of the UK GDPR are as follows:
- 9(2)(a) – Explicit consent of the data subject
- 9(2)(b) – Processing is necessary for the purposes of carrying out the obligations and exercising specific rights of the controller or of the data subject in the field of employment and social security and social protection law
- 9(2)(f) – Processing is necessary for the establishment, exercise or defence of legal claims or whenever courts are acting in their judicial capacity
- 9(2)(g) – Processing is necessary for reasons of substantial public interest
- 9(2)(h) – Processing is necessary for the purposes of occupational medicine, for the assessment of the working capacity of the employee, or the management of health and social care systems and services
- 9(2)(j) – Processing is necessary for archiving purposes in the public interest, scientific or historical research purposes or statistical purposes
Special categories of personal data include data relating to racial or ethnic origin, political opinions, religious beliefs, sexual orientation and data concerning health.
Please note that not all of the above legal bases will apply for each type of processing activity that NHSE may undertake. However, when processing any personal data for any particular purpose, one or more of the above legal bases will apply.
We may seek your consent for some processing activities, for example for sending out invitations to you to Training events and sending out material from other government agencies. If you do not give consent for us to use your data for these purposes, we will not use your data for these purposes, but your data may still be retained by us and used by us for other processing activities based on the above lawful conditions for processing set out above.
8 INFORMATION THAT WE MAY NEED TO SEND YOU
We may occasionally have to send you information from NHSE, the Department of Health, other public authorities and government agencies about matters of policy where those policy issues impact on Training, workforce planning, or other matters related to NHSE. This is because NHSE is required by statute to exercise functions of the Secretary of State in respect of Training and workforce planning. If you prefer, you can opt out of receiving information about general matters of policy impacting on Training and workforce planning by contacting your Local Office recruitment lead or tel@hee.nhs.uk. The relevant Local Office or a representative from the relevant training platform website will provide you with further advice and guidance regarding any consequences of your request.
NHSE will not send you generic information from other public authorities and government agencies on issues of government policy.
9 TRANSFERS ABROAD
The UK GDPR imposes restrictions on the transfer of personal data outside the European Union, to third countries or international organisations, in order to ensure that the level of protection of individuals afforded by the UK GDPR is not undermined.
Your data may only be transferred abroad where NHSE is assured that a third country, a territory or one or more specific sectors in the third country, or an international organisation ensures an adequate level of protection.
10 HOW WE PROTECT YOUR PERSONAL DATA
Our processing of all personal data complies with the UK GDPR principles. We have put in place appropriate security measures to prevent your personal data from being accidentally lost, used or accessed in an unauthorised way, altered or disclosed. The security of the data is assured through the implementation of NHSE’s policies on information governance management.
The personal data we hold may be held as an electronic record on data systems managed by NHSE, or as a paper record. These records are only accessed, seen and used in the following circumstances:
- if required and/or permitted by law; or
- by NHSE staff who need access to them so they can do their jobs and who are subject to a duty of confidentiality; or
- by other partner organisations, including our suppliers, who have been asked to sign appropriate non-disclosure or data sharing agreements and will never be allowed to use the information for commercial purposes.
We make every effort to keep your personal information accurate and up to date, but in some cases we are reliant on you as the data subject to notify us of any necessary changes to your personal data. If you tell us of any changes in your circumstances, we can update the records with personal data you choose to share with us.
Information collected by NHSE will never be sold for financial gain or shared with other organisations for commercial purposes.
We have put in place procedures to deal with any suspected personal data breach and will notify you and any applicable regulator of a breach where we are legally required to do so.
11 SHARING PERSONAL DATA
So we can provide the right services at the right level, we may share your personal data within services across NHSE and with other third party organisations such as the Department of Health, higher education institutions, clinical placement providers, colleges, faculties, other NHSE Local Offices, the General Medical Council, NHS Trusts/Health Boards/Health and Social Care Trusts, approved academic researchers and other NHS and government agencies where necessary, to provide the best possible Training and to ensure that we discharge NHSEs responsibilities for employment and workforce planning for the NHS. This will be on a legitimate need to know basis only.
We may also share information, where necessary, to prevent, detect or assist in the investigation of fraud or criminal activity, to assist in the administration of justice, for the purposes of seeking legal advice or exercising or defending legal rights or as otherwise required by the law.
Where the data is used for analysis and publication by a recipient or third party, any publication will be on an anonymous basis, and will not make it possible to identify any individual. This will mean that the data ceases to become personal data.
12 HOW LONG WE RETAIN YOUR PERSONAL DATA
We will keep personal data for no longer than necessary to fulfil the purposes we collected it for, in accordance with our records management policy and the NHS records retention schedule within the NHS Records Management Code of Practice at: https://www.england.nhs.uk/contact-us/privacy-notice/nhs-england-as-a-data-controller (as may be amended from time to time).
In some circumstances you can ask us to delete your data. Please see the “Your rights” section below for further information.
In some circumstances we will anonymise your personal data (so that it can no longer be associated with you) for research or statistical purposes, in which case we may use this information indefinitely without further notice to you.
13 OPEN DATA
Open data is data that is published by central government, local authorities and public bodies to help you build products and services. NHSE policy is to observe the Cabinet Office transparency and accountability commitments towards more open use of public data in accordance with relevant and applicable UK legislation.
NHSE would never share personal data through the open data facility. To this end, NHSE will implement information governance protocols that reflect the ICO’s recommended best practice for record anonymisation, and Office of National Statistics guidance on publication of statistical information.
14 YOUR RIGHTS
14.1 Right to rectification and erasure
Under the UK GDPR you have the right to rectification of inaccurate personal data and the right to request the erasure of your personal data. However, the right to erasure is not an absolute right and it may be that it is necessary for NHSE to continue to process your personal data for a number of lawful and legitimate reasons.
14.2 Right to object and withdraw your consent
You have the right in certain circumstances to ask NHSE to stop processing your personal data in relation to any NHSE service. As set out above, you can decide that you do not wish to receive information from NHSE about matters of policy affecting Training and workforce. However, the right to object is not an absolute right and it may be that it is necessary in certain circumstances for NHSE to continue to process your personal data for a number of lawful and legitimate reasons.
If you object to the way in which NHSE is processing your personal information or if you wish to ask NHSE to stop processing your personal data, please contact your relevant Local Office.
Please note, if we do stop processing personal data about you, this may prevent NHSE from providing the best possible service to you. Withdrawing your consent will result in your Training account being anonymised and access to the Training removed.
14.3 Right to request access
You can access a copy of the information NHSE holds about you by writing to NHSE’s Public and Parliamentary Accountability Team. This information is generally available to you free of charge subject to the receipt of appropriate identification. More information about subject access requests can be found here: https://www.hee.nhs.uk/about/contact-us/subject-access-request.
14.4 Right to request a transfer
The UK GDPR sets out the right for a data subject to have their personal data ported from one controller to another on request in certain circumstances. You should discuss any request for this with your Local Office. This right only applies to automated information which you initially provided consent for us to use or where we used the information to perform a contract with you.
14.5 Right to restrict processing
You can ask us to suspend the processing of your personal data if you want us to establish the data’s accuracy, where our use of the data is unlawful but you do not want us to erase it, where you need us to hold the data even if we no longer require it as you need it to establish, exercise or defend legal claims or where you have objected to our use of your data but we need to verify whether we have overriding legitimate grounds to use it.
14.6 Complaints
You have the right to make a complaint at any time to the ICO. We would, however, appreciate the chance to deal with your concerns before you approach the ICO so please contact your Local Office or the DPO in the first instance, using the contact details above.
You can contact the ICO at the following address:
The Office of the Information Commissioner
Wycliffe House
Water Lane
Wilmslow
Cheshire
SK9 5AF
14.7 Your responsibilities
It is important that you work with us to ensure that the information we hold about you is accurate and up to date so please inform NHSE if any of your personal data needs to be updated or corrected.
All communications from NHSE will normally be by email. It is therefore essential for you to maintain an effective and secure email address, or you may not receive information or other important news and information about your employment or Training.
";
+
+ Execute.Sql(@"UPDATE Config SET UpdatedDate = GETDATE() ,ConfigText =N'" + PrivacyPolicyNew + "'" +
+ "where ConfigName='PrivacyPolicy' AND IsHtml = 1");
+
+ var AcceptableUseNew = @"ACCEPTABLE USE POLICY
+
+ -
+ General
+
+ - This Acceptable Use Policy sets out how we permit you to use any of our Platforms. Your compliance with this Acceptable Use Policy is a condition of your use of the Platform.
+ - Capitalised terms have the meaning given to them in the terms of use for the Platform which are available at https://www.dls.nhs.uk/v2/LearningSolutions/Terms.
+
+
+ -
+ Acceptable use
+
+ - You are permitted to use the Platform as set out in the Terms and for the purpose of personal study.
+ - You must not use any part of the Content on the Platform for commercial purposes without obtaining a licence to do so from us or our licensors.
+ - If you print off, copy, download, share or repost any part of the Platform in breach of this Acceptable Use Policy, your right to use the Platform will cease immediately and you must, at our option, return or destroy any copies of the materials you have made.
+ - Our status (and that of any identified contributors) as the authors of Content on the Platform must always be acknowledged (except in respect of Third-Party Content).
+
+
+ -
+ Prohibited uses
+
+ -
+ You may not use the Platform:
+
+ - in any way that breaches any applicable local, national or international law or regulation;
+ - in any way that is unlawful or fraudulent or has any unlawful or fraudulent purpose or effect;
+ - in any way that infringes the rights of, or restricts or inhibits the use and enjoyment of this site by any third party;
+ - for the purpose of harming or attempting to harm minors in any way;
+ - to bully, insult, intimidate or humiliate any person;
+ - to send, knowingly receive, upload, download, use or re-use any material which does not comply with our Content Standards as set out in paragraph 4;
+ - to transmit, or procure the sending of, any unsolicited or unauthorised advertising or promotional material or any other form of similar solicitation (spam), or any unwanted or repetitive content that may cause disruption to the Platform or diminish the user experience, of the Platform’s usefulness or relevant to others;
+ - to do any act or thing with the intention of disrupting the Platform in any way, including uploading any malware or links to malware, or introduce any virus, trojan, worm, logic bomb or other material that is malicious or technologically harmful or other potentially damaging items into the Platform;
+ - to knowingly transmit any data, send or upload any material that contains viruses, Trojan horses, worms, time-bombs, keystroke loggers, spyware, adware or any other harmful programs or similar computer code designed to adversely affect the operation of any computer software or hardware; or
+ - to upload terrorist content.
+
+
+ -
+ You also agree:
+
+ - to follow any reasonable instructions given to you by us in connection with your use of the Platform;
+ - to respect the rights and dignity of others, in order to maintain the ethos and good reputation of the NHS, the public good generally and the spirit of cooperation between those studying and working within the health and care sector. In particular, you must act in a professional manner with regard to all other users of the Platform at all times;
+ -
+ not to modify or attempt to modify any of the Content, save:
+
+ - in respect of Contributions;
+ - where you are the editor of a catalogue within the Learning Hub, you may alter Content within that catalogue;
+
+
+ - not to download or copy any of the Content to electronic or photographic media;
+ - not to reproduce any part of the Content by any means or under any format other than as a reasonable aid to your personal study;
+ - not to reproduce, duplicate, copy or re-sell any Content in contravention of the provisions of this Acceptable Use Policy; and
+ - not to use tools that automatically perform actions on your behalf;
+ - not to upload any content that infringes the intellectual property rights, privacy rights or any other rights of any person or organisation; and
+ - not to attempt to disguise your identity or that of your organisation;
+ -
+ not to access without authority, interfere with, damage or disrupt:
+
+ - any part of the Platform;
+ - any equipment or network on which the Platform is stored;
+ - any software used in the provision of the Platform;
+ - the server on which the Platform is stored;
+ - any computer or database connected to the Platform; or
+ - any equipment or network or software owned or used by any third party.
+
+
+ - not to attack the Platform via a denial-of-service attack or a distributed denial-of-service attack.
+
+
+
+
+ -
+ Content standards
+
+ - The content standards set out in this paragraph 4 (Content Standards) apply to any and all Contributions.
+ - The Content Standards must be complied with in spirit as well as to the letter. The Content Standards apply to each part of any Contribution as well as to its whole.
+ - We will determine, in our discretion, whether a Contribution breaches the Content Standards.
+ -
+ A Contribution must:
+
+ - be accurate (where it states facts);
+ - be genuinely held (where it states opinions); and
+ - comply with the law applicable in England and Wales and in any country from which it is posted.
+
+
+ -
+ A Contribution must not:
+
+ -
+ contain misinformation that is likely to harm users, patients/service users, health and care workers, or the general public’s wellbeing, safety, trust and reputation, including the reputation of the NHS or any part of it. This could include false and misleading information relating to disease prevention and treatment, conspiracy theories, content that encourages discrimination, harassment or physical violence, content originating from misinformation campaigns, and content edited or manipulated in such a way as to constitute misinformation;
+
+ -
+ contain any content or link to any content:
+
+ - which is created for advertising, promotional or other commercial purposes, including links, logos and business names;
+ - which requires a subscription or payment to gain access to such content;
+ - in which the user has a commercial interest;
+ - which promotes a business name and/or logo;
+ - which contains a link to an app via iOS or Google Play; or
+ - which has as its purpose or effect the collection and sharing of personal data;
+
+
+ -
+ be irrelevant to the purpose or aims of the Platform or while addressing relevant subject matter, contain an irrelevant, unsuitable or inappropriate slant (for example relating to potentially controversial opinions or beliefs of any kind intended to influence others);
+
+ - be defamatory of any person;
+ - be obscene, offensive, hateful or inflammatory, or contain any profanity;
+ - bully, insult, intimidate or humiliate;
+ -
+ encourage suicide, substance abuse, eating disorders or other acts of self-harm.Content related to self - harm for the purposes of therapy, education and the promotion of general wellbeing may be uploaded, but we reserve the right to make changes to the way in which it is accessed in order that users do not view it accidentally;
+
+ -
+ feature sexual imagery purely intended to stimulate sexual arousal. Non - pornographic content relating to sexual health and related issues, surgical procedures and the results of surgical procedures, breastfeeding, therapy, education and the promotion of general wellbeing may be uploaded, but we reserve the right to make changes to the way in which it is accessed in order that users do not view it accidentally;
+
+ -
+ include child sexual abuse material. Content relating to safeguarding which addresses the subject of child sexual abuse may be uploaded, but we reserve the right to make changes to the way in which it is accessed in order that users do not view it accidentally;
+
+ -
+ incite or glorify violence including content designed principally for the purposes of causing reactions of shock or disgust;
+
+ -
+ promote discrimination or discriminate in respect of the protected characteristics set out in the Equality Act 2010, being age, disability, gender reassignment, marriage and civil partnership, pregnancy and maternity, race, nationality, religion or belief, sex, and sexual orientation;
+
+ - infringe any copyright, database right or trade mark of any other person;
+ - be likely to deceive any person;
+ - breach any legal duty owed to a third party, such as a contractual duty or a duty of confidence;
+ -
+ promote any illegal content or activity, including but not limited to the encouragement, promotion, justification, praise or provision of aid to dangerous persons or organisations, including extremists, terrorists and terrorist organisations and those engaged in any form of criminal activity;
+
+ - be in contempt of court;
+ -
+ be threatening, abuse or invade another''s privacy, or cause annoyance, inconvenience or needless anxiety;
+
+ - be likely to harass, bully, shame, degrade, upset, embarrass, alarm or annoy any other person;
+ - impersonate any person or misrepresent your identity or affiliation with any person;
+ -
+ advocate, promote, incite any party to commit, or assist any unlawful or criminal act such as (by way of example only) copyright infringement or computer misuse;
+
+ -
+ contain a statement which you know or believe, or have reasonable grounds for believing, that members of the public to whom the statement is, or is to be, published are likely to understand as a direct or indirect encouragement or other inducement to the commission, preparation or instigation of acts of terrorism;
+
+ - contain harmful material;
+ - give the impression that the Contribution emanates from us, if this is not the case; or
+ - disclose any third party’s confidential information, identity, personally identifiable information or personal data (including data concerning health).
+
+
+ - You acknowledge and accept that, when using the Platform and accessing the Content, some Content that has been uploaded by third parties may be factually inaccurate, or the topics addressed by the Content may be offensive, indecent, or objectionable in nature. We are not responsible (legally or otherwise) for any claim you may have in relation to the Content.
+ - When producing Content to upload to the Platform, we encourage you to implement NICE guideline recommendations and ensure that sources of evidence are valid (for example, by peer review).
+
+
+ -
+ Metadata
+
When making any Contribution, you must where prompted include a sufficient description of the Content so that other users can understand the description, source, and age of the Content. For example, if Content has been quality assured, then the relevant information should be posted in the appropriate field. All metadata fields on the Platform must be completed appropriately before initiating upload. Including the correct information is important in order to help other users locate the Content (otherwise the Content may not appear in search results for others to select).
+
+ -
+ Updates
+
You must update each Contribution at least once every 3 (three) years, or update or remove it should it cease to be relevant or become outdated or revealed or generally perceived to be unsafe or otherwise unsuitable for inclusion on the Platform.
+
+ -
+ Accessibility
+
Where practicable, all Contributions should aim to meet the accessibility standards as described in our Accessibility Statement
+ [https://www.dls.nhs.uk/v2/LearningSolutions/AccessibilityHelp] and as set out in the AA Standard Web Content Accessibility Guidelines v2.1 found here:
+ https://www.w3.org/TR/WCAG21/.
+
+ -
+ Rules about linking to the Platform
+
+ - The Platform must not be framed on any other site.
+ - You may directly link to any Content that is hosted on the Platform, however, please be aware that not all links will continue to be available indefinitely. We will use our best efforts to ensure that all links are valid at the time of creating the related Content but cannot be held responsible for any subsequent changes to the link address or related Content.
+
+
+ -
+ No text or data mining, or web scraping
+
+ -
+ You shall not conduct, facilitate, authorize or permit any text or data mining or web scraping in relation to the Platform or any services provided via, or in relation to, the Platform. This includes using (or permitting, authorizing or attempting the use of):
+
+ - any ""robot"", ""bot"", ""spider"", ""scraper"" or other automated device, program, tool, algorithm, code, process or methodology to access, obtain, copy, monitor or republish any portion of the Platform or any data, Content, information or services accessed via the same; and/or
+ - any automated analytical technique aimed at analyzing text and data in digital form to generate information which includes but is not limited to patterns, trends, and correlations.
+
+
+ - The provisions in this paragraph should be treated as an express reservation of our rights in this regard, including for the purposes of Article 4(3) of Digital Copyright Directive ((EU) 2019/790).
+ - This paragraph shall not apply insofar as (but only to the extent that) we are unable to exclude or limit text or data mining or web scraping activity by contract under the laws which are applicable to us.
+
+
+ -
+ Breach of this Acceptable Use Policy
+
Failure to comply with this Acceptable Use Policy constitutes a material breach of this Acceptable Use Policy upon which you are permitted to use the Platform and may result in our taking all or any of the following actions:
+
+ - Immediate, temporary, or permanent withdrawal of your right to use the Platform;
+ - Immediate, temporary, or permanent removal of any Contribution uploaded by you to the Platform;
+ - Issue of a warning to you;
+ - Legal proceedings against you for reimbursement of all costs...
+ - Disclosure of such information to law enforcement authorities...
+ - Any other action we reasonably deem appropriate.
+
+
+
";
+
+ Execute.Sql(@"UPDATE Config SET UpdatedDate = GETDATE() ,ConfigText =N'" + AcceptableUseNew + "'" +
+ "where ConfigName='AcceptableUse' AND IsHtml = 1");
+ }
+ public override void Down()
+ {
+ var PrivacyPolicyOld = @"PRIVACY NOTICE
This page explains our privacy policy and how we will use and protect any information about you that you give to us or that we collate when you visit this website, or undertake employment with NHS England (NHSE or we/us/our), or participate in any NHSE sponsored training, education and development including via any of our training platform websites (Training).
This privacy notice is intended to provide transparency regarding what personal data NHSE may hold about you, how it will be processed and stored, how long it will be retained and who may have access to your data.
Personal data is any information relating to an identified or identifiable living person (known as the data subject). An identifiable person is one who can be identified, directly or indirectly, in particular by reference to an identifier such as a name, an identification number or factors specific to the physical, genetic or mental identity of that person, for example.
1 OUR ROLE IN THE NHS
We are here to improve the quality of healthcare for the people and patients of England through education, training and lifelong development of staff and appropriate planning of the workforce required to deliver healthcare services in England.
We aim to enable high quality, effective, compassionate care and to identify the right people with the right skills and the right values. All the information we collect is to support these objectives.
2 IMPORTANT INFORMATION
NHSE is the data controller in respect of any personal data it holds concerning trainees in Training, individuals employed by NHSE and individuals accessing NHSE’s website.
We have appointed a data protection officer (DPO) who is responsible for overseeing questions in relation to this privacy policy. If you have any questions about this privacy policy or our privacy practices, want to know more about how your information will be used, or make a request to exercise your legal rights, please contact our DPO in the following ways:
Name: Andrew Todd
Email address: gdpr@hee.nhs.uk
Postal address: NHS England of Skipton House, 80 London Road, London SE1 6LH
3 WHAT THIS PRIVACY STATEMENT COVERS
This privacy statement only covers the processing of personal data by NHSE that NHSE has obtained from data subjects accessing any of NHSE’s websites and from its provision of services and exercise of functions. It does not cover the processing of data by any sites that can be linked to or from NHSE’s websites, so you should always be aware when you are moving to another site and read the privacy statement on that website.
When providing NHSE with any of your personal data for the first time, for example, when you take up an appointment with NHSE or when you register for any Training, you will be asked to confirm that you have read and accepted the terms of this privacy statement. A copy of your acknowledgement will be retained for reference.
If our privacy policy changes in any way, we will place an updated version on this page. Regularly reviewing the page ensures you are always aware of what information we collect, how we use it and under what circumstances, if any, we will share it with other parties.
4 WHY NHSE COLLECTS YOUR PERSONAL DATA
Personal data may be collected from you via the recruitment process, when you register and/or create an account for any Training, during your Annual Review of Competence Progression or via NHSE’s appraisal process. Personal data may also be obtained from Local Education Providers or employing organisations in connection with the functions of NHSE.
Your personal data is collected and processed for the purposes of and in connection with the functions that NHSE performs with regard to Training and planning. The collection and processing of such data is necessary for the purposes of those functions.
A full copy of our notification to the Information Commissioner’s Office (ICO) (the UK regulator for data protection issues), can be found on the ICO website here: www.ico.org.uk by searching NHSE’s ICO registration number, which is Z2950066.
In connection with Training, NHSE collects and uses your personal information for the following purposes:
- to manage your Training and programme, including allowing you to access your own learning history;
- to quality assure Training programmes and ensure that standards are maintained, including gathering feedback or input on the service, content, or layout of the Training and customising the content and/or layout of the Training;
- to identify workforce planning targets;
- to maintain patient safety through the management of performance concerns;
- to comply with legal and regulatory responsibilities including revalidation;
- to contact you about Training updates, opportunities, events, surveys and information that may be of interest to you;
- transferring your Training activity records for programmes to other organisations involved in medical training in the healthcare sector. These organisations include professional bodies that you may be a member of, such as a medical royal college or foundation school; or employing organisations, such as trusts;
- making your Training activity records visible to specific named individuals, such as tutors, to allow tutors to view their trainees’ activity. We would seek your explicit consent before authorising anyone else to view your records;
- providing anonymous, summarised data to partner organisations, such as professional bodies; or local organisations, such as strategic health authorities or trusts;
- for NHSE internal review;
- to provide HR related support services and Training to you, for clinical professional learner recruitment;
- to promote our services;
- to monitor our own accounts and records;
- to monitor our work, to report on progress made; and
- to let us fulfil our statutory obligations and statutory returns as set by the Department of Health and the law (for example complying with NHSE’s legal obligations and regulatory responsibilities under employment law).
Further information about our use of your personal data in connection with Training can be found in ’A Reference Guide for Postgraduate Foundation and Specialty Training in the UK’, published by the Conference of Postgraduate Medical Deans of the United Kingdom and known as the ‘Gold Guide’, which can be found here: https://www.copmed.org.uk/gold-guide.
5 TYPES OF PERSONAL DATA COLLECTED BY NHSE
The personal data that NHSE collects when you register for Training enables the creation of an accurate user profile/account, which is necessary for reporting purposes and to offer Training that is relevant to your needs.
The personal data that is stored by NHSE is limited to information relating to your work, such as your job role, place of work, and membership number for a professional body (e.g. your General Medical Council number). NHSE will never ask for your home address or any other domestic information.
When accessing Training, you will be asked to set up some security questions, which may contain personal information. These questions enable you to log in if you forget your password and will never be used for any other purpose. The answers that you submit when setting up these security questions are encrypted in the database so no one can view what has been entered, not even NHSE administrators.
NHSE also store a record of some Training activity, including upload and download of Training content, posts on forums or other communication media, and all enquires to the service desks that support the Training.
If you do not provide personal data that we need from you when requested, we may not be able to provide services (such as Training) to you. In this case, we may have to cancel such service, but we will notify you at the time if this happens.
6 COOKIES
When you access NHSE’s website and Training, we want to make them easy, useful and reliable. This sometimes involves placing small amounts of limited information on your device (such as your computer or mobile phone). These small files are known as cookies, and we ask you to agree to their usage in accordance with ICO guidance.
These cookies are used to improve the services (including the Training) we provide you through, for example:
- enabling a service to recognise your device, so you do not have to give the same information several times during one task (e.g. we use a cookie to remember your username if you check the ’Remember Me’ box on a log in page);
- recognising that you may already have given a username and password, so you do not need to do it for every web page requested;
- measuring how many people are using services, so they can be made easier to use and there is enough capacity to ensure they are fast; and
- analysing anonymised data to help us understand how people interact with services so we can make them better.
We use a series of cookies to monitor website speed and usage, as well as to ensure that any preferences you have selected previously are the same when you return to our website. Please visit our cookie policies page to understand the cookies that we use: https://www.dls.nhs.uk/v2/CookieConsent/CookiePolicy
Most cookies applied when accessing Training are used to keep track of your input when filling in online forms, known as session-ID cookies, which are exempt from needing consent as they are deemed essential for using the website or Training they apply to. Some cookies, like those used to measure how you use the Training, are not needed for our website to work. These cookies can help us improve the Training, but we’ll only use them if you say it’s OK. We’ll use a cookie to save your settings.
On a number of pages on our website or Training, we use ’plug-ins’ or embedded media. For example, we might embed YouTube videos. Where we have used this type of content, the suppliers of these services may also set cookies on your device when you visit their pages. These are known as ’third-party’ cookies. To opt-out of third-parties collecting any data regarding your interaction on our website, please refer to their websites for further information.
We will not use cookies to collect personal data about you. However, if you wish to restrict or block the cookies which are set by our websites or Training, or indeed any other website, you can do this through your browser settings. The ’Help’ function within your browser should tell you how. Alternatively, you may wish to visit www.aboutcookies.org which contains comprehensive information on how to do this on a wide variety of browsers. You will also find details on how to delete cookies from your machine as well as more general information about cookies. Please be aware that restricting cookies may impact on the functionality of our website.
7 LEGAL BASIS FOR PROCESSING
The retained EU law version of the General Data Protection Regulation ((EU) 2016/679) (UK GDPR) requires that data controllers and organisations that process personal data demonstrate compliance with its provisions. This involves publishing our basis for lawful processing of personal data.
As personal data is processed for the purposes of NHSE’s statutory functions, NHSE’s legal bases for the processing of personal data as listed in Article 6 of the UK GDPR are as follows:
- 6(1)(a) – Consent of the data subject
- 6(1)(b) – Processing is necessary for the performance of a contract to which the data subject is party or in order to take steps at the request of the data subject prior to entering into a contract
- 6(1)(c) – Processing is necessary for compliance with a legal obligation
- 6(1)(e) – Processing is necessary for the performance of a task carried out in the public interest or in the exercise of official authority vested in the controller
Where NHSE processes special categories of personal data, its additional legal bases for processing such data as listed in Article 9 of the UK GDPR are as follows:
- 9(2)(a) – Explicit consent of the data subject
- 9(2)(b) – Processing is necessary for the purposes of carrying out the obligations and exercising specific rights of the controller or of the data subject in the field of employment and social security and social protection law
- 9(2)(f) – Processing is necessary for the establishment, exercise or defence of legal claims or whenever courts are acting in their judicial capacity
- 9(2)(g) – Processing is necessary for reasons of substantial public interest
- 9(2)(h) – Processing is necessary for the purposes of occupational medicine, for the assessment of the working capacity of the employee, or the management of health and social care systems and services
- 9(2)(j) – Processing is necessary for archiving purposes in the public interest, scientific or historical research purposes or statistical purposes
Special categories of personal data include data relating to racial or ethnic origin, political opinions, religious beliefs, sexual orientation and data concerning health.
Please note that not all of the above legal bases will apply for each type of processing activity that NHSE may undertake. However, when processing any personal data for any particular purpose, one or more of the above legal bases will apply.
We may seek your consent for some processing activities, for example for sending out invitations to you to Training events and sending out material from other government agencies. If you do not give consent for us to use your data for these purposes, we will not use your data for these purposes, but your data may still be retained by us and used by us for other processing activities based on the above lawful conditions for processing set out above.
8 INFORMATION THAT WE MAY NEED TO SEND YOU
We may occasionally have to send you information from NHSE, the Department of Health, other public authorities and government agencies about matters of policy where those policy issues impact on Training, workforce planning, or other matters related to NHSE. This is because NHSE is required by statute to exercise functions of the Secretary of State in respect of Training and workforce planning. If you prefer, you can opt out of receiving information about general matters of policy impacting on Training and workforce planning by contacting your Local Office recruitment lead or tel@hee.nhs.uk. The relevant Local Office or a representative from the relevant training platform website will provide you with further advice and guidance regarding any consequences of your request.
NHSE will not send you generic information from other public authorities and government agencies on issues of government policy.
9 TRANSFERS ABROAD
The UK GDPR imposes restrictions on the transfer of personal data outside the European Union, to third countries or international organisations, in order to ensure that the level of protection of individuals afforded by the UK GDPR is not undermined.
Your data may only be transferred abroad where NHSE is assured that a third country, a territory or one or more specific sectors in the third country, or an international organisation ensures an adequate level of protection.
10 HOW WE PROTECT YOUR PERSONAL DATA
Our processing of all personal data complies with the UK GDPR principles. We have put in place appropriate security measures to prevent your personal data from being accidentally lost, used or accessed in an unauthorised way, altered or disclosed. The security of the data is assured through the implementation of NHSE’s policies on information governance management.
The personal data we hold may be held as an electronic record on data systems managed by NHSE, or as a paper record. These records are only accessed, seen and used in the following circumstances:
- if required and/or permitted by law; or
- by NHSE staff who need access to them so they can do their jobs and who are subject to a duty of confidentiality; or
- by other partner organisations, including our suppliers, who have been asked to sign appropriate non-disclosure or data sharing agreements and will never be allowed to use the information for commercial purposes.
We make every effort to keep your personal information accurate and up to date, but in some cases we are reliant on you as the data subject to notify us of any necessary changes to your personal data. If you tell us of any changes in your circumstances, we can update the records with personal data you choose to share with us.
Information collected by NHSE will never be sold for financial gain or shared with other organisations for commercial purposes.
We have put in place procedures to deal with any suspected personal data breach and will notify you and any applicable regulator of a breach where we are legally required to do so.
11 SHARING PERSONAL DATA
So we can provide the right services at the right level, we may share your personal data within services across NHSE and with other third party organisations such as the Department of Health, higher education institutions, clinical placement providers, colleges, faculties, other NHSE Local Offices, the General Medical Council, NHS Trusts/Health Boards/Health and Social Care Trusts, approved academic researchers and other NHS and government agencies where necessary, to provide the best possible Training and to ensure that we discharge NHSEs responsibilities for employment and workforce planning for the NHS. This will be on a legitimate need to know basis only.
We may also share information, where necessary, to prevent, detect or assist in the investigation of fraud or criminal activity, to assist in the administration of justice, for the purposes of seeking legal advice or exercising or defending legal rights or as otherwise required by the law.
Where the data is used for analysis and publication by a recipient or third party, any publication will be on an anonymous basis, and will not make it possible to identify any individual. This will mean that the data ceases to become personal data.
12 HOW LONG WE RETAIN YOUR PERSONAL DATA
We will keep personal data for no longer than necessary to fulfil the purposes we collected it for, in accordance with our records management policy and the NHS records retention schedule within the NHS Records Management Code of Practice at: https://www.england.nhs.uk/contact-us/privacy-notice/nhs-england-as-a-data-controller (as may be amended from time to time).
In some circumstances you can ask us to delete your data. Please see the “Your rights” section below for further information.
In some circumstances we will anonymise your personal data (so that it can no longer be associated with you) for research or statistical purposes, in which case we may use this information indefinitely without further notice to you.
13 OPEN DATA
Open data is data that is published by central government, local authorities and public bodies to help you build products and services. NHSE policy is to observe the Cabinet Office transparency and accountability commitments towards more open use of public data in accordance with relevant and applicable UK legislation.
NHSE would never share personal data through the open data facility. To this end, NHSE will implement information governance protocols that reflect the ICO’s recommended best practice for record anonymisation, and Office of National Statistics guidance on publication of statistical information.
14 YOUR RIGHTS
14.1 Right to rectification and erasure
Under the UK GDPR you have the right to rectification of inaccurate personal data and the right to request the erasure of your personal data. However, the right to erasure is not an absolute right and it may be that it is necessary for NHSE to continue to process your personal data for a number of lawful and legitimate reasons.
14.2 Right to object and withdraw your consent
You have the right in certain circumstances to ask NHSE to stop processing your personal data in relation to any NHSE service. As set out above, you can decide that you do not wish to receive information from NHSE about matters of policy affecting Training and workforce. However, the right to object is not an absolute right and it may be that it is necessary in certain circumstances for NHSE to continue to process your personal data for a number of lawful and legitimate reasons.
If you object to the way in which NHSE is processing your personal information or if you wish to ask NHSE to stop processing your personal data, please contact your relevant Local Office.
Please note, if we do stop processing personal data about you, this may prevent NHSE from providing the best possible service to you. Withdrawing your consent will result in your Training account being anonymised and access to the Training removed.
14.3 Right to request access
You can access a copy of the information NHSE holds about you by writing to NHSE’s Public and Parliamentary Accountability Team. This information is generally available to you free of charge subject to the receipt of appropriate identification. More information about subject access requests can be found here: https://www.hee.nhs.uk/about/contact-us/subject-access-request.
14.4 Right to request a transfer
The UK GDPR sets out the right for a data subject to have their personal data ported from one controller to another on request in certain circumstances. You should discuss any request for this with your Local Office. This right only applies to automated information which you initially provided consent for us to use or where we used the information to perform a contract with you.
14.5 Right to restrict processing
You can ask us to suspend the processing of your personal data if you want us to establish the data’s accuracy, where our use of the data is unlawful but you do not want us to erase it, where you need us to hold the data even if we no longer require it as you need it to establish, exercise or defend legal claims or where you have objected to our use of your data but we need to verify whether we have overriding legitimate grounds to use it.
14.6 Complaints
You have the right to make a complaint at any time to the ICO. We would, however, appreciate the chance to deal with your concerns before you approach the ICO so please contact your Local Office or the DPO in the first instance, using the contact details above.
You can contact the ICO at the following address:
The Office of the Information Commissioner
Wycliffe House
Water Lane
Wilmslow
Cheshire
SK9 5AF
14.7 Your responsibilities
It is important that you work with us to ensure that the information we hold about you is accurate and up to date so please inform NHSE if any of your personal data needs to be updated or corrected.
All communications from NHSE will normally be by email. It is therefore essential for you to maintain an effective and secure email address, or you may not receive information or other important news and information about your employment or Training.
";
+
+ Execute.Sql(@"UPDATE Config SET UpdatedDate = GETDATE() ,ConfigText =N'" + PrivacyPolicyOld + "'" +
+ "where ConfigName='PrivacyPolicy' AND IsHtml = 1");
+
+ var AcceptableUseOld = @"ACCEPTABLE USE POLICY
+
+ -
+ General
+
+ - This Acceptable Use Policy sets out how we permit you to use any of our Platforms. Your compliance with this Acceptable Use Policy is a condition of your use of the Platform.
+ - Capitalised terms have the meaning given to them in the terms of use for the Platform which are available at https://www.dls.nhs.uk/v2/LearningSolutions/Terms.
+
+
+ -
+ Acceptable use
+
+ - You are permitted to use the Platform as set out in the Terms and for the purpose of personal study.
+ - You must not use any part of the Content on the Platform for commercial purposes without obtaining a licence to do so from us or our licensors.
+ - If you print off, copy, download, share or repost any part of the Platform in breach of this Acceptable Use Policy, your right to use the Platform will cease immediately and you must, at our option, return or destroy any copies of the materials you have made.
+ - Our status (and that of any identified contributors) as the authors of Content on the Platform must always be acknowledged (except in respect of Third-Party Content).
+
+
+ -
+ Prohibited uses
+
+ -
+ You may not use the Platform:
+
+ - in any way that breaches any applicable local, national or international law or regulation;
+ - in any way that is unlawful or fraudulent or has any unlawful or fraudulent purpose or effect;
+ - in any way that infringes the rights of, or restricts or inhibits the use and enjoyment of this site by any third party;
+ - for the purpose of harming or attempting to harm minors in any way;
+ - to bully, insult, intimidate or humiliate any person;
+ - to send, knowingly receive, upload, download, use or re-use any material which does not comply with our Content Standards as set out in paragraph 4;
+ - to transmit, or procure the sending of, any unsolicited or unauthorised advertising or promotional material or any other form of similar solicitation (spam), or any unwanted or repetitive content that may cause disruption to the Platform or diminish the user experience, of the Platform’s usefulness or relevant to others;
+ - to do any act or thing with the intention of disrupting the Platform in any way, including uploading any malware or links to malware, or introduce any virus, trojan, worm, logic bomb or other material that is malicious or technologically harmful or other potentially damaging items into the Platform;
+ - to knowingly transmit any data, send or upload any material that contains viruses, Trojan horses, worms, time-bombs, keystroke loggers, spyware, adware or any other harmful programs or similar computer code designed to adversely affect the operation of any computer software or hardware; or
+ - to upload terrorist content.
+
+
+ -
+ You also agree:
+
+ - to follow any reasonable instructions given to you by us in connection with your use of the Platform;
+ - to respect the rights and dignity of others, in order to maintain the ethos and good reputation of the NHS, the public good generally and the spirit of cooperation between those studying and working within the health and care sector. In particular, you must act in a professional manner with regard to all other users of the Platform at all times;
+ -
+ not to modify or attempt to modify any of the Content, save:
+
+ - in respect of Contributions;
+ - where you are the editor of a catalogue within the Learning Hub, you may alter Content within that catalogue;
+
+
+ - not to download or copy any of the Content to electronic or photographic media;
+ - not to reproduce any part of the Content by any means or under any format other than as a reasonable aid to your personal study;
+ - not to reproduce, duplicate, copy or re-sell any Content in contravention of the provisions of this Acceptable Use Policy; and
+ - not to use tools that automatically perform actions on your behalf;
+ - not to upload any content that infringes the intellectual property rights, privacy rights or any other rights of any person or organisation; and
+ - not to attempt to disguise your identity or that of your organisation;
+ -
+ not to access without authority, interfere with, damage or disrupt:
+
+ - any part of the Platform;
+ - any equipment or network on which the Platform is stored;
+ - any software used in the provision of the Platform;
+ - the server on which the Platform is stored;
+ - any computer or database connected to the Platform; or
+ - any equipment or network or software owned or used by any third party.
+
+
+ - not to attack the Platform via a denial-of-service attack or a distributed denial-of-service attack.
+
+
+
+
+ -
+ Content standards
+
+ - The content standards set out in this paragraph 4 (Content Standards) apply to any and all Contributions.
+ - The Content Standards must be complied with in spirit as well as to the letter. The Content Standards apply to each part of any Contribution as well as to its whole.
+ - We will determine, in our discretion, whether a Contribution breaches the Content Standards.
+ -
+ A Contribution must:
+
+ - be accurate (where it states facts);
+ - be genuinely held (where it states opinions); and
+ - comply with the law applicable in England and Wales and in any country from which it is posted.
+
+
+ -
+ A Contribution must not:
+
+ -
+ contain misinformation that is likely to harm users, patients/service users, health and care workers, or the general public’s wellbeing, safety, trust and reputation, including the reputation of the NHS or any part of it. This could include false and misleading information relating to disease prevention and treatment, conspiracy theories, content that encourages discrimination, harassment or physical violence, content originating from misinformation campaigns, and content edited or manipulated in such a way as to constitute misinformation;
+
+ -
+ contain any content or link to any content:
+
+ - which is created for advertising, promotional or other commercial purposes, including links, logos and business names;
+ - which requires a subscription or payment to gain access to such content;
+ - in which the user has a commercial interest;
+ - which promotes a business name and/or logo;
+ - which contains a link to an app via iOS or Google Play; or
+ - which has as its purpose or effect the collection and sharing of personal data;
+
+
+ -
+ be irrelevant to the purpose or aims of the Platform or while addressing relevant subject matter, contain an irrelevant, unsuitable or inappropriate slant (for example relating to potentially controversial opinions or beliefs of any kind intended to influence others);
+
+ - be defamatory of any person;
+ - be obscene, offensive, hateful or inflammatory, or contain any profanity;
+ - bully, insult, intimidate or humiliate;
+ -
+ encourage suicide, substance abuse, eating disorders or other acts of self-harm.Content related to self - harm for the purposes of therapy, education and the promotion of general wellbeing may be uploaded, but we reserve the right to make changes to the way in which it is accessed in order that users do not view it accidentally;
+
+ -
+ feature sexual imagery purely intended to stimulate sexual arousal. Non - pornographic content relating to sexual health and related issues, surgical procedures and the results of surgical procedures, breastfeeding, therapy, education and the promotion of general wellbeing may be uploaded, but we reserve the right to make changes to the way in which it is accessed in order that users do not view it accidentally;
+
+ -
+ include child sexual abuse material. Content relating to safeguarding which addresses the subject of child sexual abuse may be uploaded, but we reserve the right to make changes to the way in which it is accessed in order that users do not view it accidentally;
+
+ -
+ incite or glorify violence including content designed principally for the purposes of causing reactions of shock or disgust;
+
+ -
+ promote discrimination or discriminate in respect of the protected characteristics set out in the Equality Act 2010, being age, disability, gender reassignment, marriage and civil partnership, pregnancy and maternity, race, nationality, religion or belief, sex, and sexual orientation;
+
+ - infringe any copyright, database right or trade mark of any other person;
+ - be likely to deceive any person;
+ - breach any legal duty owed to a third party, such as a contractual duty or a duty of confidence;
+ -
+ promote any illegal content or activity, including but not limited to the encouragement, promotion, justification, praise or provision of aid to dangerous persons or organisations, including extremists, terrorists and terrorist organisations and those engaged in any form of criminal activity;
+
+ - be in contempt of court;
+ -
+ be threatening, abuse or invade another''s privacy, or cause annoyance, inconvenience or needless anxiety;
+
+ - be likely to harass, bully, shame, degrade, upset, embarrass, alarm or annoy any other person;
+ - impersonate any person or misrepresent your identity or affiliation with any person;
+ -
+ advocate, promote, incite any party to commit, or assist any unlawful or criminal act such as (by way of example only) copyright infringement or computer misuse;
+
+ -
+ contain a statement which you know or believe, or have reasonable grounds for believing, that members of the public to whom the statement is, or is to be, published are likely to understand as a direct or indirect encouragement or other inducement to the commission, preparation or instigation of acts of terrorism;
+
+ - contain harmful material;
+ - give the impression that the Contribution emanates from us, if this is not the case; or
+ - disclose any third party’s confidential information, identity, personally identifiable information or personal data (including data concerning health).
+
+
+ - You acknowledge and accept that, when using the Platform and accessing the Content, some Content that has been uploaded by third parties may be factually inaccurate, or the topics addressed by the Content may be offensive, indecent, or objectionable in nature. We are not responsible (legally or otherwise) for any claim you may have in relation to the Content.
+ - When producing Content to upload to the Platform, we encourage you to implement NICE guideline recommendations and ensure that sources of evidence are valid (for example, by peer review).
+
+
+ -
+ Metadata
+
+ When making any Contribution, you must where prompted include a sufficient description of the Content so that other users can understand the description, source, and age of the Content. For example, if Content has been quality assured, then the relevant information should be posted in the appropriate field. All metadata fields on the Platform must be completed appropriately before initiating upload. Including the correct information is important in order to help other users locate the Content (otherwise the Content may not appear in search results for others to select).
+
+
+ -
+ Updates
+
+ You must update each Contribution at least once every 3 (three) years, or update or remove it should it cease to be relevant or become outdated or revealed or generally perceived to be unsafe or otherwise unsuitable for inclusion on the Platform.
+
+
+ -
+ Accessibility
+
+ Where practicable, all Contributions should aim to meet the accessibility standards as described in our Accessibility Statement
+ [https://www.dls.nhs.uk/v2/LearningSolutions/AccessibilityHelp] and as set out in the AA Standard Web Content Accessibility Guidelines v2.1 found here:
+ https://www.w3.org/TR/WCAG21/.
+
+
+ -
+ Rules about linking to the Platform
+
+ - The Platform must not be framed on any other site.
+ - You may directly link to any Content that is hosted on the Platform, however, please be aware that not all links will continue to be available indefinitely. We will use our best efforts to ensure that all links are valid at the time of creating the related Content but cannot be held responsible for any subsequent changes to the link address or related Content.
+
+
+ -
+ No text or data mining, or web scraping
+
+ -
+ You shall not conduct, facilitate, authorize or permit any text or data mining or web scraping in relation to the Platform or any services provided via, or in relation to, the Platform. This includes using (or permitting, authorizing or attempting the use of):
+
+ - any ""robot"", ""bot"", ""spider"", ""scraper"" or other automated device, program, tool, algorithm, code, process or methodology to access, obtain, copy, monitor or republish any portion of the Platform or any data, Content, information or services accessed via the same; and/or
+ - any automated analytical technique aimed at analyzing text and data in digital form to generate information which includes but is not limited to patterns, trends, and correlations.
+
+
+ - The provisions in this paragraph should be treated as an express reservation of our rights in this regard, including for the purposes of Article 4(3) of Digital Copyright Directive ((EU) 2019/790).
+ - This paragraph shall not apply insofar as (but only to the extent that) we are unable to exclude or limit text or data mining or web scraping activity by contract under the laws which are applicable to us.
+
+
+ -
+ Breach of this Acceptable Use Policy
+
+ Failure to comply with this Acceptable Use Policy constitutes a material breach of this Acceptable Use Policy upon which you are permitted to use the Platform and may result in our taking all or any of the following actions:
+ - immediate, temporary, or permanent withdrawal of your right to use the Platform;
+ - immediate, temporary, or permanent removal of any Contribution uploaded by you to the Platform;
+ - issue of a warning to you;
+ - legal proceedings against you for reimbursement of all costs on an indemnity basis (including, but not limited to, reasonable administrative and legal costs) resulting from the breach, and/or further legal action against you;
+ - disclosure of such information to law enforcement authorities as we reasonably feel is necessary or as required by law; and/or
+ - any other action we reasonably deem appropriate.
+
+
+
";
+
+ Execute.Sql(@"UPDATE Config SET UpdatedDate = GETDATE() ,ConfigText =N'" + AcceptableUseOld + "'" +
+ "where ConfigName='AcceptableUse' AND IsHtml = 1");
+ }
+ }
+}
diff --git a/DigitalLearningSolutions.Data.Migrations/202508181154_CreateOrAlterMoveCompetenciesAndGroups.cs b/DigitalLearningSolutions.Data.Migrations/202508181154_CreateOrAlterMoveCompetenciesAndGroups.cs
new file mode 100644
index 0000000000..60ac1b3cfb
--- /dev/null
+++ b/DigitalLearningSolutions.Data.Migrations/202508181154_CreateOrAlterMoveCompetenciesAndGroups.cs
@@ -0,0 +1,19 @@
+namespace DigitalLearningSolutions.Data.Migrations
+{
+ using FluentMigrator;
+ [Migration(202508181154)]
+ public class CreateOrAlterMoveCompetenciesAndGroups : Migration
+ {
+ public override void Up()
+ {
+ Execute.Sql(Properties.Resources.TD_483_uspMoveCompetencyInSelfAssessmentCreateOrAlter_UP);
+ Execute.Sql(Properties.Resources.TD_483_uspMoveCompetencyGroupInSelfAssessmentCreateOrAlter_UP);
+ }
+ public override void Down()
+ {
+ Execute.Sql("DROP PROCEDURE IF EXISTS [dbo].[usp_MoveCompetencyGroupInSelfAssessment]");
+ Execute.Sql("DROP PROCEDURE IF EXISTS [dbo].[usp_MoveCompetencyInSelfAssessment]");
+ Execute.Sql("DROP PROCEDURE IF EXISTS [dbo].[usp_RenumberSelfAssessmentStructure]");
+ }
+ }
+}
diff --git a/DigitalLearningSolutions.Data.Migrations/202510021000_AddIsdeletedSelfAssessmentCollaborators.cs b/DigitalLearningSolutions.Data.Migrations/202510021000_AddIsdeletedSelfAssessmentCollaborators.cs
new file mode 100644
index 0000000000..870707eb79
--- /dev/null
+++ b/DigitalLearningSolutions.Data.Migrations/202510021000_AddIsdeletedSelfAssessmentCollaborators.cs
@@ -0,0 +1,16 @@
+namespace DigitalLearningSolutions.Data.Migrations
+{
+ using FluentMigrator;
+ [Migration(202510021000)]
+ public class AddIsdeletedSelfAssessmentCollaborators : Migration
+ {
+ public override void Up()
+ {
+ Alter.Table("SelfAssessmentCollaborators").AddColumn("IsDeleted").AsBoolean().WithDefaultValue(false);
+ }
+ public override void Down()
+ {
+ Delete.Column("IsDeleted").FromTable("SelfAssessmentCollaborators");
+ }
+ }
+}
diff --git a/DigitalLearningSolutions.Data.Migrations/202510221132_AddIncludeLearnerDeclarationPromptToSelfAssessmentsTable.cs b/DigitalLearningSolutions.Data.Migrations/202510221132_AddIncludeLearnerDeclarationPromptToSelfAssessmentsTable.cs
new file mode 100644
index 0000000000..fbb0e4ee35
--- /dev/null
+++ b/DigitalLearningSolutions.Data.Migrations/202510221132_AddIncludeLearnerDeclarationPromptToSelfAssessmentsTable.cs
@@ -0,0 +1,18 @@
+namespace DigitalLearningSolutions.Data.Migrations
+{
+ using FluentMigrator;
+
+ [Migration(202510221132)]
+ public class AddIncludeLearnerDeclarationPromptToSelfAssessmentsTable : Migration
+ {
+ public override void Up()
+ {
+ Alter.Table("SelfAssessments").AddColumn("IncludeLearnerDeclarationPrompt").AsBoolean().WithDefaultValue(false);
+ }
+
+ public override void Down()
+ {
+ Delete.Column("IncludeLearnerDeclarationPrompt").FromTable("SelfAssessments");
+ }
+ }
+}
diff --git a/DigitalLearningSolutions.Data.Migrations/202511241125_Alter_usp_MoveCompetencyInSelfAssessment.cs b/DigitalLearningSolutions.Data.Migrations/202511241125_Alter_usp_MoveCompetencyInSelfAssessment.cs
new file mode 100644
index 0000000000..691a82b9ee
--- /dev/null
+++ b/DigitalLearningSolutions.Data.Migrations/202511241125_Alter_usp_MoveCompetencyInSelfAssessment.cs
@@ -0,0 +1,19 @@
+
+
+namespace DigitalLearningSolutions.Data.Migrations
+{
+ using FluentMigrator;
+
+ [Migration(202511241125)]
+ public class Alter_usp_MoveCompetencyInSelfAssessment : Migration
+ {
+ public override void Up()
+ {
+ Execute.Sql(Properties.Resources.TD_483_Alter_usp_MoveCompetencyInSelfAssessment_Up);
+ }
+ public override void Down()
+ {
+ Execute.Sql(Properties.Resources.TD_483_Alter_usp_MoveCompetencyInSelfAssessment_Down);
+ }
+ }
+}
diff --git a/DigitalLearningSolutions.Data.Migrations/202601091000_SupervisorDashboardIndexes.cs b/DigitalLearningSolutions.Data.Migrations/202601091000_SupervisorDashboardIndexes.cs
new file mode 100644
index 0000000000..52faab12b0
--- /dev/null
+++ b/DigitalLearningSolutions.Data.Migrations/202601091000_SupervisorDashboardIndexes.cs
@@ -0,0 +1,71 @@
+namespace DigitalLearningSolutions.Data.Migrations
+{
+ using FluentMigrator;
+ using FluentMigrator.SqlServer;
+
+ [Migration(202601091000)]
+ public class SupervisorDashboardIndexes : Migration
+ {
+ public override void Up()
+ {
+ Create.Index("IX_SupervisorDelegates_Admin_Removed")
+ .OnTable("SupervisorDelegates")
+ .OnColumn("SupervisorAdminID").Ascending()
+ .OnColumn("Removed").Ascending()
+ .WithOptions()
+ .NonClustered()
+ .Include("DelegateUserID");
+
+ Create.Index("IX_CandidateAssessmentSupervisors_SupervisorDelegateId_Removed")
+ .OnTable("CandidateAssessmentSupervisors")
+ .OnColumn("SupervisorDelegateId").Ascending()
+ .OnColumn("Removed").Ascending()
+ .WithOptions()
+ .NonClustered()
+ .Include("CandidateAssessmentID");
+
+ Create.Index("IX_CandidateAssessments_RemovedDate")
+ .OnTable("CandidateAssessments")
+ .OnColumn("RemovedDate").Ascending()
+ .WithOptions()
+ .NonClustered()
+ .Include("SelfAssessmentID")
+ .Include("DelegateUserID");
+
+ Create.Index("IX_CandidateAssessmentSupervisorVerifications_CandidateAssessmentSupervisorID_Verified")
+ .OnTable("CandidateAssessmentSupervisorVerifications")
+ .OnColumn("CandidateAssessmentSupervisorID").Ascending()
+ .OnColumn("Verified").Ascending()
+ .WithOptions()
+ .NonClustered();
+
+ Create.Index("IX_SRSV_CandidateAssessmentSupervisorID_SelfAssessmentResultId_Superceded_Verified")
+ .OnTable("SelfAssessmentResultSupervisorVerifications")
+ .OnColumn("CandidateAssessmentSupervisorID").Ascending()
+ .OnColumn("SelfAssessmentResultId").Ascending()
+ .OnColumn("Superceded").Ascending()
+ .OnColumn("Verified").Ascending()
+ .WithOptions()
+ .NonClustered()
+ .Include("Requested");
+
+ Create.Index("IX_SelfAssessmentResults_SelfAssessmentID_Result")
+ .OnTable("SelfAssessmentResults")
+ .OnColumn("SelfAssessmentID").Ascending()
+ .OnColumn("Result").Ascending()
+ .WithOptions()
+ .NonClustered()
+ .Include("CompetencyID")
+ .Include("DateTime");
+ }
+ public override void Down()
+ {
+ Delete.Index("IX_SupervisorDelegates_Admin_Removed").OnTable("SupervisorDelegates");
+ Delete.Index("IX_CandidateAssessmentSupervisors_SupervisorDelegateId_Removed").OnTable("CandidateAssessmentSupervisors");
+ Delete.Index("IX_CandidateAssessments_RemovedDate").OnTable("CandidateAssessments");
+ Delete.Index("IX_CASVerifications_CandidateAssessmentSupervisorID_Verified").OnTable("CandidateAssessmentSupervisorVerifications");
+ Delete.Index("IX_SRSV_CandidateAssessmentSupervisorID_SelfAssessmentResultId_Superceded_Verified").OnTable("SelfAssessmentResultSupervisorVerifications");
+ Delete.Index("IX_SelfAssessmentResults_SelfAssessmentID_Result").OnTable("SelfAssessmentResults");
+ }
+ }
+}
diff --git a/DigitalLearningSolutions.Data.Migrations/202602091540_AlterViewAdminUsersAddAdminUserId.cs b/DigitalLearningSolutions.Data.Migrations/202602091540_AlterViewAdminUsersAddAdminUserId.cs
new file mode 100644
index 0000000000..5287079d7b
--- /dev/null
+++ b/DigitalLearningSolutions.Data.Migrations/202602091540_AlterViewAdminUsersAddAdminUserId.cs
@@ -0,0 +1,17 @@
+using FluentMigrator;
+
+namespace DigitalLearningSolutions.Data.Migrations
+{
+ [Migration(202602091540)]
+ public class AlterViewAdminUsersAddAdminUserId : Migration
+ {
+ public override void Up()
+ {
+ Execute.Sql(Properties.Resources.TD_6866_AlterViewAdminUsersAddAdminUserId_Up);
+ }
+ public override void Down()
+ {
+ Execute.Sql(Properties.Resources.TD_6866_AlterViewAdminUsersAddAdminUserId_Down);
+ }
+ }
+}
diff --git a/DigitalLearningSolutions.Data.Migrations/Properties/Resources.Designer.cs b/DigitalLearningSolutions.Data.Migrations/Properties/Resources.Designer.cs
index ca62e703c2..51adc5e370 100644
--- a/DigitalLearningSolutions.Data.Migrations/Properties/Resources.Designer.cs
+++ b/DigitalLearningSolutions.Data.Migrations/Properties/Resources.Designer.cs
@@ -2237,6 +2237,116 @@ internal static string TD_4634_Alter_GetCompletedCoursesForCandidate_UP {
}
}
+ ///
+ /// Looks up a localized string similar to /****** Object: StoredProcedure [dbo].[usp_MoveCompetencyInSelfAssessment] Script Date: 24/11/2025 10:56:08 ******/
+ ///SET ANSI_NULLS ON
+ ///GO
+ ///
+ ///SET QUOTED_IDENTIFIER ON
+ ///GO
+ ///
+ ///ALTER PROCEDURE [dbo].[usp_MoveCompetencyInSelfAssessment]
+ /// @SelfAssessmentID INT,
+ /// @CompetencyID INT,
+ /// @Direction NVARCHAR(10)
+ ///AS
+ ///BEGIN
+ /// SET NOCOUNT ON;
+ ///
+ /// DECLARE @GroupID INT, @CurrentOrder INT;
+ ///
+ /// SELECT
+ /// @GroupID = CompetencyGroupID,
+ /// @CurrentOrder = Ordering
+ /// FROM SelfAssessment [rest of string was truncated]";.
+ ///
+ internal static string TD_483_Alter_usp_MoveCompetencyInSelfAssessment_Down {
+ get {
+ return ResourceManager.GetString("TD_483_Alter_usp_MoveCompetencyInSelfAssessment_Down", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to /****** Object: StoredProcedure [dbo].[usp_MoveCompetencyInSelfAssessment] Script Date: 24/11/2025 10:56:08 ******/
+ ///SET ANSI_NULLS ON
+ ///GO
+ ///
+ ///SET QUOTED_IDENTIFIER ON
+ ///GO
+ ///
+ ///ALTER PROCEDURE [dbo].[usp_MoveCompetencyInSelfAssessment]
+ /// @SelfAssessmentID INT,
+ /// @CompetencyID INT,
+ /// @Direction NVARCHAR(10)
+ ///AS
+ ///BEGIN
+ /// SET NOCOUNT ON;
+ ///
+ /// DECLARE @CurrentOrder INT;
+ ///
+ /// SELECT
+ /// @CurrentOrder = Ordering
+ /// FROM SelfAssessmentStructure
+ /// WHERE SelfAssessmentID = @SelfAssessme [rest of string was truncated]";.
+ ///
+ internal static string TD_483_Alter_usp_MoveCompetencyInSelfAssessment_Up {
+ get {
+ return ResourceManager.GetString("TD_483_Alter_usp_MoveCompetencyInSelfAssessment_Up", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to
+ ///CREATE OR ALTER PROCEDURE usp_RenumberSelfAssessmentStructure
+ /// @SelfAssessmentID INT
+ ///AS
+ ///BEGIN
+ /// SET NOCOUNT ON;
+ ///
+ /// /*
+ /// Step 1: Build an ordered list of groups
+ /// - Each group is ranked by its current Min(Ordering)
+ /// - Ungrouped competencies (NULL CompetencyGroupID) are treated as their own "pseudo group"
+ /// */
+ /// ;WITH GroupRanks AS (
+ /// SELECT
+ /// CompetencyGroupID,
+ /// ROW_NUMBER() OVER (ORDER BY MIN(Ordering)) AS GroupRank
+ /// FROM Sel [rest of string was truncated]";.
+ ///
+ internal static string TD_483_uspMoveCompetencyGroupInSelfAssessmentCreateOrAlter_UP {
+ get {
+ return ResourceManager.GetString("TD-483-uspMoveCompetencyGroupInSelfAssessmentCreateOrAlter_UP", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to CREATE OR ALTER PROCEDURE usp_MoveCompetencyInSelfAssessment
+ /// @SelfAssessmentID INT,
+ /// @CompetencyID INT,
+ /// @Direction NVARCHAR(10)
+ ///AS
+ ///BEGIN
+ /// SET NOCOUNT ON;
+ ///
+ /// DECLARE @GroupID INT, @CurrentOrder INT;
+ ///
+ /// SELECT
+ /// @GroupID = CompetencyGroupID,
+ /// @CurrentOrder = Ordering
+ /// FROM SelfAssessmentStructure
+ /// WHERE SelfAssessmentID = @SelfAssessmentID AND CompetencyID = @CompetencyID;
+ ///
+ /// IF @GroupID IS NULL
+ /// BEGIN
+ /// -- Can't reorder ungrouped competencies [rest of string was truncated]";.
+ ///
+ internal static string TD_483_uspMoveCompetencyInSelfAssessmentCreateOrAlter_UP {
+ get {
+ return ResourceManager.GetString("TD-483-uspMoveCompetencyInSelfAssessmentCreateOrAlter_UP", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to /****** Object: StoredProcedure [dbo].[GetActivitiesForDelegateEnrolment] Script Date: 22/10/2024 16:55:08 ******/
///SET ANSI_NULLS ON
@@ -2773,6 +2883,56 @@ internal static string TD_6437_usp_GetSelfAssessmentReport_Up {
}
}
+ ///
+ /// Looks up a localized string similar to /****** Object: View [dbo].[AdminUsers] Script Date: 09/02/2026 15:48:13 ******/
+ ///SET ANSI_NULLS ON
+ ///GO
+ ///
+ ///SET QUOTED_IDENTIFIER ON
+ ///GO
+ ///
+ ///-- =============================================
+ ///-- Author:
+ ///-- Modified date: 02/06/2023
+ ///-- Description: Return the admin user details
+ ///-- =============================================
+ ///
+ ///ALTER VIEW [dbo].[AdminUsers] AS
+ ///SELECT dbo.AdminAccounts.ID AS AdminID,
+ /// null AS Login,
+ /// dbo.Users.Passwor [rest of string was truncated]";.
+ ///
+ internal static string TD_6866_AlterViewAdminUsersAddAdminUserId_Down {
+ get {
+ return ResourceManager.GetString("TD_6866_AlterViewAdminUsersAddAdminUserId_Down", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to /****** Object: View [dbo].[AdminUsers] Script Date: 09/02/2026 15:48:13 ******/
+ ///SET ANSI_NULLS ON
+ ///GO
+ ///
+ ///SET QUOTED_IDENTIFIER ON
+ ///GO
+ ///
+ ///-- =============================================
+ ///-- Author:
+ ///-- Modified date: 02/06/2023
+ ///-- Description: Return the admin user details
+ ///-- =============================================
+ ///
+ ///ALTER VIEW [dbo].[AdminUsers] AS
+ ///SELECT dbo.AdminAccounts.ID AS AdminID,
+ /// dbo.AdminAccounts.UserID AS AdminUserID,
+ /// null [rest of string was truncated]";.
+ ///
+ internal static string TD_6866_AlterViewAdminUsersAddAdminUserId_Up {
+ get {
+ return ResourceManager.GetString("TD_6866_AlterViewAdminUsersAddAdminUserId_Up", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to /****** Object: StoredProcedure [dbo].[GetActiveAvailableCustomisationsForCentreFiltered_V6] Script Date: 29/09/2022 19:11:04 ******/
///SET ANSI_NULLS ON
diff --git a/DigitalLearningSolutions.Data.Migrations/Properties/Resources.resx b/DigitalLearningSolutions.Data.Migrations/Properties/Resources.resx
index 8c63d14460..c71a4e2d2b 100644
--- a/DigitalLearningSolutions.Data.Migrations/Properties/Resources.resx
+++ b/DigitalLearningSolutions.Data.Migrations/Properties/Resources.resx
@@ -499,6 +499,12 @@
..\Scripts\TD-5759_CreateOrAlterSelfAssessmentReportSPandTVF-Fix_UP.sql;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8
+
+ ..\Scripts\TD-483-uspMoveCompetencyInSelfAssessmentCreateOrAlter_UP.sql;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8
+
+
+ ..\Scripts\TD-483-uspMoveCompetencyGroupInSelfAssessmentCreateOrAlter_UP.sql;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8
+
..\Scripts\TD-5535-Alter_GetActivitiesForDelegateEnrolment_Down.sql;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-16
@@ -514,10 +520,22 @@
..\Scripts\TD-5552-Alter_SendRetiringSelfAssessmentNotification_Up.sql;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-16
+
+ ..\Scripts\TD-483-Alter_usp_MoveCompetencyInSelfAssessment_Down.sql;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-16
+
+
+ ..\Scripts\TD-483-Alter_usp_MoveCompetencyInSelfAssessment_Up.sql;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-16
+
..\Scripts\TD-6437-usp_GetSelfAssessmentReport_Down.sql;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-16
..\Scripts\TD-6437-usp_GetSelfAssessmentReport_Up.sql;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-16
+
+ ..\Scripts\TD-6866-AlterViewAdminUsersAddAdminUserId_Down.sql;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;Windows-1252
+
+
+ ..\Scripts\TD-6866-AlterViewAdminUsersAddAdminUserId_Up.sql;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;Windows-1252
+
\ No newline at end of file
diff --git a/DigitalLearningSolutions.Data.Migrations/Scripts/TD-483-Alter_usp_MoveCompetencyInSelfAssessment_Down.sql b/DigitalLearningSolutions.Data.Migrations/Scripts/TD-483-Alter_usp_MoveCompetencyInSelfAssessment_Down.sql
new file mode 100644
index 0000000000..0b6668e913
Binary files /dev/null and b/DigitalLearningSolutions.Data.Migrations/Scripts/TD-483-Alter_usp_MoveCompetencyInSelfAssessment_Down.sql differ
diff --git a/DigitalLearningSolutions.Data.Migrations/Scripts/TD-483-Alter_usp_MoveCompetencyInSelfAssessment_Up.sql b/DigitalLearningSolutions.Data.Migrations/Scripts/TD-483-Alter_usp_MoveCompetencyInSelfAssessment_Up.sql
new file mode 100644
index 0000000000..50e97004f2
Binary files /dev/null and b/DigitalLearningSolutions.Data.Migrations/Scripts/TD-483-Alter_usp_MoveCompetencyInSelfAssessment_Up.sql differ
diff --git a/DigitalLearningSolutions.Data.Migrations/Scripts/TD-483-uspMoveCompetencyGroupInSelfAssessmentCreateOrAlter_UP.sql b/DigitalLearningSolutions.Data.Migrations/Scripts/TD-483-uspMoveCompetencyGroupInSelfAssessmentCreateOrAlter_UP.sql
new file mode 100644
index 0000000000..f42d8894c7
--- /dev/null
+++ b/DigitalLearningSolutions.Data.Migrations/Scripts/TD-483-uspMoveCompetencyGroupInSelfAssessmentCreateOrAlter_UP.sql
@@ -0,0 +1,164 @@
+
+CREATE OR ALTER PROCEDURE usp_RenumberSelfAssessmentStructure
+ @SelfAssessmentID INT
+AS
+BEGIN
+ SET NOCOUNT ON;
+
+ /*
+ Step 1: Build an ordered list of groups
+ - Each group is ranked by its current Min(Ordering)
+ - Ungrouped competencies (NULL CompetencyGroupID) are treated as their own "pseudo group"
+ */
+ ;WITH GroupRanks AS (
+ SELECT
+ CompetencyGroupID,
+ ROW_NUMBER() OVER (ORDER BY MIN(Ordering)) AS GroupRank
+ FROM SelfAssessmentStructure
+ WHERE SelfAssessmentID = @SelfAssessmentID
+ GROUP BY CompetencyGroupID
+ )
+ /*
+ Step 2: Renumber groups and competencies
+ - Groups get ranked (1,2,3)
+ - Within each group, competencies are ordered by their current Ordering and renumbered (1,2,3)
+ */
+ UPDATE sas
+ SET sas.Ordering = rn.NewOrdering
+ FROM SelfAssessmentStructure sas
+ INNER JOIN (
+ SELECT
+ s.ID,
+ -- new group position * 1000 + row within group
+ -- gives room between groups and avoids clashes
+ (g.GroupRank * 1000) +
+ ROW_NUMBER() OVER (PARTITION BY s.CompetencyGroupID, g.GroupRank ORDER BY s.Ordering, s.ID) AS NewOrdering
+ FROM SelfAssessmentStructure s
+ INNER JOIN GroupRanks g
+ ON g.CompetencyGroupID = s.CompetencyGroupID
+ WHERE s.SelfAssessmentID = @SelfAssessmentID
+ ) rn ON sas.ID = rn.ID;
+
+END
+
+GO
+
+CREATE OR ALTER PROCEDURE usp_MoveCompetencyGroupInSelfAssessment
+ @SelfAssessmentID INT,
+ @GroupID INT,
+ @Direction NVARCHAR(10) -- 'up' or 'down'
+AS
+BEGIN
+ SET NOCOUNT ON;
+ SET XACT_ABORT ON;
+
+ BEGIN TRY
+ BEGIN TRAN;
+
+ /* 1) Rank groups by current Min(Ordering) (NULL groups excluded here; include if desired). */
+ ;WITH GroupRanks AS (
+ SELECT
+ CompetencyGroupID,
+ MIN(Ordering) AS MinOrder,
+ ROW_NUMBER() OVER (
+ ORDER BY MIN(Ordering), MIN(CompetencyGroupID)
+ ) AS RankPos
+ FROM SelfAssessmentStructure
+ WHERE SelfAssessmentID = @SelfAssessmentID
+ AND CompetencyGroupID IS NOT NULL
+ GROUP BY CompetencyGroupID
+ )
+ SELECT *
+ INTO #Groups
+ FROM GroupRanks;
+
+ DECLARE @CurRank INT, @SwapRank INT, @SwapGroupID INT;
+
+ SELECT @CurRank = RankPos
+ FROM #Groups
+ WHERE CompetencyGroupID = @GroupID;
+
+ IF @CurRank IS NULL
+ BEGIN
+ DROP TABLE #Groups;
+ COMMIT TRAN; RETURN; -- nothing to do
+ END
+
+ IF LOWER(@Direction) = 'up'
+ SET @SwapRank = @CurRank - 1;
+ ELSE IF LOWER(@Direction) = 'down'
+ SET @SwapRank = @CurRank + 1;
+ ELSE
+ BEGIN
+ DROP TABLE #Groups;
+ ROLLBACK TRAN; THROW 50000, 'Direction must be ''up'' or ''down''.', 1;
+ END
+
+ SELECT @SwapGroupID = CompetencyGroupID
+ FROM #Groups
+ WHERE RankPos = @SwapRank;
+
+ IF @SwapGroupID IS NULL
+ BEGIN
+ DROP TABLE #Groups;
+ COMMIT TRAN; RETURN; -- already at top/bottom
+ END
+
+ /* 2) Build a mapping where ONLY the two groups swap ranks; others keep theirs. */
+ SELECT
+ g.CompetencyGroupID,
+ CASE
+ WHEN g.CompetencyGroupID = @GroupID THEN @SwapRank
+ WHEN g.CompetencyGroupID = @SwapGroupID THEN @CurRank
+ ELSE g.RankPos
+ END AS NewRank
+ INTO #RankMap
+ FROM #Groups g;
+
+ /* 3) Choose a block size big enough to keep groups separated when we recompute.
+ Using count(rows in this self assessment) + 10 is a safe dynamic choice. */
+ DECLARE @Block INT =
+ (SELECT COUNT(*) FROM SelfAssessmentStructure WHERE SelfAssessmentID = @SelfAssessmentID) + 10;
+
+ /* 4) Recompute EVERY rows Ordering from the rank map (this sets the new global order).
+ We preserve within-group relative order using the existing Ordering (and ID as tiebreak). */
+ ;WITH NewOrders AS (
+ SELECT
+ s.ID,
+ (m.NewRank * @Block)
+ + ROW_NUMBER() OVER (
+ PARTITION BY s.CompetencyGroupID
+ ORDER BY s.Ordering, s.ID
+ ) AS NewOrdering
+ FROM SelfAssessmentStructure s
+ JOIN #RankMap m
+ ON m.CompetencyGroupID = s.CompetencyGroupID
+ WHERE s.SelfAssessmentID = @SelfAssessmentID
+ )
+ UPDATE s
+ SET s.Ordering = n.NewOrdering
+ FROM SelfAssessmentStructure s
+ JOIN NewOrders n ON n.ID = s.ID;
+
+ /* 5) (Optional) Compress to 1..N while keeping the just-established relative order. */
+ ;WITH Ordered AS (
+ SELECT ID,
+ ROW_NUMBER() OVER (ORDER BY Ordering, ID) AS Seq
+ FROM SelfAssessmentStructure
+ WHERE SelfAssessmentID = @SelfAssessmentID
+ )
+ UPDATE s
+ SET s.Ordering = o.Seq
+ FROM SelfAssessmentStructure s
+ JOIN Ordered o ON o.ID = s.ID;
+
+ DROP TABLE #Groups;
+ DROP TABLE #RankMap;
+
+ COMMIT TRAN;
+ END TRY
+ BEGIN CATCH
+ IF @@TRANCOUNT > 0 ROLLBACK TRAN;
+ THROW;
+ END CATCH
+END;
diff --git a/DigitalLearningSolutions.Data.Migrations/Scripts/TD-483-uspMoveCompetencyInSelfAssessmentCreateOrAlter_UP.sql b/DigitalLearningSolutions.Data.Migrations/Scripts/TD-483-uspMoveCompetencyInSelfAssessmentCreateOrAlter_UP.sql
new file mode 100644
index 0000000000..176422b549
--- /dev/null
+++ b/DigitalLearningSolutions.Data.Migrations/Scripts/TD-483-uspMoveCompetencyInSelfAssessmentCreateOrAlter_UP.sql
@@ -0,0 +1,59 @@
+CREATE OR ALTER PROCEDURE usp_MoveCompetencyInSelfAssessment
+ @SelfAssessmentID INT,
+ @CompetencyID INT,
+ @Direction NVARCHAR(10)
+AS
+BEGIN
+ SET NOCOUNT ON;
+
+ DECLARE @GroupID INT, @CurrentOrder INT;
+
+ SELECT
+ @GroupID = CompetencyGroupID,
+ @CurrentOrder = Ordering
+ FROM SelfAssessmentStructure
+ WHERE SelfAssessmentID = @SelfAssessmentID AND CompetencyID = @CompetencyID;
+
+ IF @GroupID IS NULL
+ BEGIN
+ -- Can't reorder ungrouped competencies via group-based logic
+ RETURN;
+ END
+
+ DECLARE @TargetCompetencyID INT, @TargetOrder INT;
+
+ IF @Direction = 'up'
+ BEGIN
+ SELECT TOP 1
+ @TargetCompetencyID = CompetencyID,
+ @TargetOrder = Ordering
+ FROM SelfAssessmentStructure
+ WHERE SelfAssessmentID = @SelfAssessmentID
+ AND CompetencyGroupID = @GroupID
+ AND Ordering < @CurrentOrder
+ ORDER BY Ordering DESC;
+ END
+ ELSE IF @Direction = 'down'
+ BEGIN
+ SELECT TOP 1
+ @TargetCompetencyID = CompetencyID,
+ @TargetOrder = Ordering
+ FROM SelfAssessmentStructure
+ WHERE SelfAssessmentID = @SelfAssessmentID
+ AND CompetencyGroupID = @GroupID
+ AND Ordering > @CurrentOrder
+ ORDER BY Ordering ASC;
+ END
+
+ IF @TargetCompetencyID IS NOT NULL
+ BEGIN
+ -- Swap the orderings
+ UPDATE SelfAssessmentStructure
+ SET Ordering = @TargetOrder
+ WHERE SelfAssessmentID = @SelfAssessmentID AND CompetencyID = @CompetencyID;
+
+ UPDATE SelfAssessmentStructure
+ SET Ordering = @CurrentOrder
+ WHERE SelfAssessmentID = @SelfAssessmentID AND CompetencyID = @TargetCompetencyID;
+ END
+END
diff --git a/DigitalLearningSolutions.Data.Migrations/Scripts/TD-6866-AlterViewAdminUsersAddAdminUserId_Down.sql b/DigitalLearningSolutions.Data.Migrations/Scripts/TD-6866-AlterViewAdminUsersAddAdminUserId_Down.sql
new file mode 100644
index 0000000000..554164472c
--- /dev/null
+++ b/DigitalLearningSolutions.Data.Migrations/Scripts/TD-6866-AlterViewAdminUsersAddAdminUserId_Down.sql
@@ -0,0 +1,56 @@
+/****** Object: View [dbo].[AdminUsers] Script Date: 09/02/2026 15:48:13 ******/
+SET ANSI_NULLS ON
+GO
+
+SET QUOTED_IDENTIFIER ON
+GO
+
+-- =============================================
+-- Author:
+-- Modified date: 02/06/2023
+-- Description: Return the admin user details
+-- =============================================
+
+ALTER VIEW [dbo].[AdminUsers] AS
+SELECT dbo.AdminAccounts.ID AS AdminID,
+ null AS Login,
+ dbo.Users.PasswordHash AS Password,
+ dbo.AdminAccounts.CentreID,
+ dbo.Centres.CentreName,
+ dbo.AdminAccounts.IsCentreAdmin AS CentreAdmin,
+ 0 AS ConfigAdmin,
+ dbo.AdminAccounts.IsReportsViewer AS SummaryReports,
+ dbo.AdminAccounts.IsSuperAdmin AS UserAdmin,
+ dbo.Users.FirstName AS Forename,
+ dbo.Users.LastName AS Surname,
+ dbo.Users.PrimaryEmail AS Email,
+ dbo.AdminAccounts.IsCentreManager,
+ 1 AS Approved,
+ 0 AS PasswordReminder,
+ '' AS EITSProfile,
+ null AS PasswordReminderHash,
+ null AS PasswordReminderDate,
+ dbo.AdminAccounts.Active,
+ dbo.AdminAccounts.IsContentManager AS ContentManager,
+ dbo.AdminAccounts.PublishToAll,
+ dbo.AdminAccounts.ImportOnly,
+ dbo.Users.TermsAgreed AS TCAgreed,
+ dbo.AdminAccounts.IsContentCreator AS ContentCreator,
+ dbo.Users.FailedLoginCount,
+ dbo.Users.ProfileImage,
+ dbo.AdminAccounts.IsSupervisor AS Supervisor,
+ dbo.AdminAccounts.IsTrainer AS Trainer,
+ ISNULL(dbo.AdminAccounts.CategoryID, 0) AS CategoryID,
+ null AS SkypeHandle,
+ 0 AS PublicSkypeLink,
+ dbo.AdminAccounts.IsFrameworkDeveloper,
+ dbo.Users.ResetPasswordID,
+ dbo.AdminAccounts.IsFrameworkContributor,
+ dbo.AdminAccounts.IsWorkforceManager,
+ dbo.AdminAccounts.IsWorkforceContributor,
+ dbo.AdminAccounts.IsLocalWorkforceManager,
+ dbo.AdminAccounts.IsNominatedSupervisor AS NominatedSupervisor
+FROM dbo.Users
+ INNER JOIN dbo.AdminAccounts ON dbo.Users.ID = dbo.AdminAccounts.UserID
+ INNER JOIN dbo.Centres ON dbo.AdminAccounts.CentreID = dbo.Centres.CentreID
+GO
diff --git a/DigitalLearningSolutions.Data.Migrations/Scripts/TD-6866-AlterViewAdminUsersAddAdminUserId_Up.sql b/DigitalLearningSolutions.Data.Migrations/Scripts/TD-6866-AlterViewAdminUsersAddAdminUserId_Up.sql
new file mode 100644
index 0000000000..e0b8e7fbe5
--- /dev/null
+++ b/DigitalLearningSolutions.Data.Migrations/Scripts/TD-6866-AlterViewAdminUsersAddAdminUserId_Up.sql
@@ -0,0 +1,57 @@
+/****** Object: View [dbo].[AdminUsers] Script Date: 09/02/2026 15:48:13 ******/
+SET ANSI_NULLS ON
+GO
+
+SET QUOTED_IDENTIFIER ON
+GO
+
+-- =============================================
+-- Author:
+-- Modified date: 02/06/2023
+-- Description: Return the admin user details
+-- =============================================
+
+ALTER VIEW [dbo].[AdminUsers] AS
+SELECT dbo.AdminAccounts.ID AS AdminID,
+ dbo.AdminAccounts.UserID AS AdminUserID,
+ null AS Login,
+ dbo.Users.PasswordHash AS Password,
+ dbo.AdminAccounts.CentreID,
+ dbo.Centres.CentreName,
+ dbo.AdminAccounts.IsCentreAdmin AS CentreAdmin,
+ 0 AS ConfigAdmin,
+ dbo.AdminAccounts.IsReportsViewer AS SummaryReports,
+ dbo.AdminAccounts.IsSuperAdmin AS UserAdmin,
+ dbo.Users.FirstName AS Forename,
+ dbo.Users.LastName AS Surname,
+ dbo.Users.PrimaryEmail AS Email,
+ dbo.AdminAccounts.IsCentreManager,
+ 1 AS Approved,
+ 0 AS PasswordReminder,
+ '' AS EITSProfile,
+ null AS PasswordReminderHash,
+ null AS PasswordReminderDate,
+ dbo.AdminAccounts.Active,
+ dbo.AdminAccounts.IsContentManager AS ContentManager,
+ dbo.AdminAccounts.PublishToAll,
+ dbo.AdminAccounts.ImportOnly,
+ dbo.Users.TermsAgreed AS TCAgreed,
+ dbo.AdminAccounts.IsContentCreator AS ContentCreator,
+ dbo.Users.FailedLoginCount,
+ dbo.Users.ProfileImage,
+ dbo.AdminAccounts.IsSupervisor AS Supervisor,
+ dbo.AdminAccounts.IsTrainer AS Trainer,
+ ISNULL(dbo.AdminAccounts.CategoryID, 0) AS CategoryID,
+ null AS SkypeHandle,
+ 0 AS PublicSkypeLink,
+ dbo.AdminAccounts.IsFrameworkDeveloper,
+ dbo.Users.ResetPasswordID,
+ dbo.AdminAccounts.IsFrameworkContributor,
+ dbo.AdminAccounts.IsWorkforceManager,
+ dbo.AdminAccounts.IsWorkforceContributor,
+ dbo.AdminAccounts.IsLocalWorkforceManager,
+ dbo.AdminAccounts.IsNominatedSupervisor AS NominatedSupervisor
+FROM dbo.Users
+ INNER JOIN dbo.AdminAccounts ON dbo.Users.ID = dbo.AdminAccounts.UserID
+ INNER JOIN dbo.Centres ON dbo.AdminAccounts.CentreID = dbo.Centres.CentreID
+GO
diff --git a/DigitalLearningSolutions.Data/ApiClients/LearningHubApiClient.cs b/DigitalLearningSolutions.Data/ApiClients/LearningHubApiClient.cs
index 6616954ef6..2faf24ae7e 100644
--- a/DigitalLearningSolutions.Data/ApiClients/LearningHubApiClient.cs
+++ b/DigitalLearningSolutions.Data/ApiClients/LearningHubApiClient.cs
@@ -88,7 +88,7 @@ IEnumerable resourceReferenceIds
public async Task GetCatalogues()
{
- var response = await GetStringAsync("/Catalogues");
+ var response = await GetStringAsync("/Catalogue");
var result = JsonConvert.DeserializeObject(response);
return result;
}
diff --git a/DigitalLearningSolutions.Data/DataServices/CompetencyAssessmentDataService.cs b/DigitalLearningSolutions.Data/DataServices/CompetencyAssessmentDataService.cs
new file mode 100644
index 0000000000..d5788c2c20
--- /dev/null
+++ b/DigitalLearningSolutions.Data/DataServices/CompetencyAssessmentDataService.cs
@@ -0,0 +1,1784 @@
+namespace DigitalLearningSolutions.Data.DataServices
+{
+ using Dapper;
+ using DigitalLearningSolutions.Data.Extensions;
+ using DigitalLearningSolutions.Data.Models.CompetencyAssessments;
+ using Microsoft.Extensions.Logging;
+ using System;
+ using System.Collections.Generic;
+ using System.Data;
+ using System.Linq;
+ public interface ICompetencyAssessmentDataService
+ {
+ //GET DATA
+ IEnumerable GetAllCompetencyAssessments(int adminId);
+
+ IEnumerable GetCompetencyAssessmentsForAdminId(int adminId);
+
+ CompetencyAssessmentBase? GetCompetencyAssessmentBaseById(int competencyAssessmentId, int adminId);
+
+ CompetencyAssessmentBase? GetCompetencyAssessmentBaseByName(string competencyAssessmentName, int adminId);
+ CompetencyAssessment? GetCompetencyAssessmentById(int competencyAssessmentId, int adminId);
+ IEnumerable GetNRPProfessionalGroups();
+ IEnumerable GetNRPSubGroups(int? nRPProfessionalGroupID);
+ IEnumerable GetNRPRoles(int? nRPSubGroupID);
+
+ CompetencyAssessmentTaskStatus GetOrInsertAndReturnAssessmentTaskStatus(int assessmentId, bool frameworkBased);
+
+ int[] GetLinkedFrameworkIds(int assessmentId);
+
+ int? GetPrimaryLinkedFrameworkId(int assessmentId);
+
+ int GetCompetencyCountByFrameworkId(int assessmentId, int frameworkId);
+
+ IEnumerable GetCompetenciesForCompetencyAssessment(int competencyAssessmentId);
+ IEnumerable GetLinkedFrameworksForCompetencyAssessment(int competencyAssessmentId);
+ int[] GetLinkedFrameworkCompetencyIds(int competencyAssessmentId, int frameworkId);
+ CompetencyAssessmentFeatures? GetCompetencyAssessmentFeaturesTaskStatus(int competencyAssessmentId);
+ int? GetSelfAssessmentStructure(int competencyAssessmentId);
+ IEnumerable GetCompetencyWithAssessmentQuestionRoleRequirements(int competencyAssessmentId, int? competencyId, int? assessmentQuestionId);
+ IEnumerable GetCompetencySelfAssessmentReviews(int competencyAssessmentId);
+ SelfAssessmentReview? GetCompetencySelfAssessmentReviewById(int competencyAssessmentId, int selfAssessmentReviewId);
+ SelfAssessmentReviewOutcomeNotification? GetSelfAssessmentReviewNotification(int reviewId);
+ //UPDATE DATA
+ bool UpdateCompetencyAssessmentName(int competencyAssessmentId, int adminId, string competencyAssessmentName);
+
+ bool UpdateCompetencyRoleProfileLinks(int competencyAssessmentId, int adminId, int? professionalGroupId, int? subGroupId, int? roleId);
+ bool UpdateCompetencyAssessmentBranding(
+ int competencyAssessmentId,
+ int adminId,
+ int brandId,
+ int categoryId
+ );
+ bool UpdateCompetencyAssessmentVocabulary(int competencyAssessmentId, int adminId, string vocabulary);
+ bool UpdateCompetencyAssessmentDescription(int competencyAssessmentId, int adminId, string competencyAssessmentDescription);
+ bool UpdateIntroductoryTextTaskStatus(int assessmentId, bool taskStatus);
+ bool UpdateBrandingTaskStatus(int assessmentId, bool taskStatus);
+ bool UpdateVocabularyTaskStatus(int assessmentId, bool taskStatus);
+ bool UpdateRoleProfileLinksTaskStatus(int assessmentId, bool taskStatus);
+ bool UpdateFrameworkLinksTaskStatus(int assessmentId, bool taskStatus, bool? previousStatus);
+ bool RemoveSelfAssessmentFramework(int assessmentId, int frameworkId, int adminId);
+ bool UpdateSelectCompetenciesTaskStatus(int assessmentId, bool taskStatus, bool? previousStatus);
+ bool UpdateOptionalCompetenciesTaskStatus(int assessmentId, bool taskStatus, bool? previousStatus);
+ bool UpdateRoleRequirementsTaskStatus(int assessmentId, bool taskStatus, bool? previousStatus);
+ bool UpdateWorkingGroupTaskStatus(int assessmentId, bool taskStatus, bool? previousStatus);
+ bool UpdateCompetencyAssessmentOptions(
+ bool includeLearnerDeclarationPrompt,
+ bool includesSignposting,
+ bool linearNavigation,
+ bool useDescriptionExpanders,
+ string? questionLabelText,
+ string? reviewerCommentsLabelText,
+ int competencyAssessmentId, int adminId);
+ bool UpdateCompetencyAssessmentOptionsTaskStatus(int assessmentId, bool taskStatus);
+ void MoveCompetencyInSelfAssessment(int competencyAssessmentId,
+ int competencyId,
+ string direction
+ );
+ void MoveCompetencyGroupInSelfAssessment(int competencyAssessmentId,
+ int groupId,
+ string direction
+ );
+ bool UpdateCompetencyAssessmentFeaturesTaskStatus(int id, bool descriptionStatus, bool providerandCategoryStatus, bool vocabularyStatus,
+ bool workingGroupStatus, bool AllframeworkCompetenciesStatus);
+ bool UpdateCompetencyAssessmentRoleRequirementsTaskStatus(int assessmentId, bool taskStatus);
+ void UpdateSelfAssessmentFromFramework(int selfAssessmentId, int? frameworkId);
+ bool UpdateOptionalCompetenciesInAssessment(int selfAssessmentId, int[] groupIds, int[] selectedStructureIds);
+ void UpdateMinimumOptionalCompetencies(int selfAssessmentId, int minimumOptionalCompetecies);
+ void UpdateManageOptionalCompetenciesPrompt(int selfAssessmentId, string manageOptionalCompetenciesPrompt);
+ bool UpdatePrimaryFrameworkCompetencies(int assessmentId, int frameworkId);
+ void UpdateRoleRequirementsFlags(int assessmentId, bool enforceRoleRequirementsForSignOff, bool includeRequirementsFilters);
+ int GetCountOfAsssessmentQuestionInCompetencyAssessment(int competencyAssessmentId, int assessmentQuestionId);
+ bool UpdateSupervisorRolesTaskStatus(int competencyAssessmentId, bool taskCompleteChecked);
+ bool UpdateSelfAssessments(int competencyAssessmentId,
+ int? signoff,
+ int? confirm,
+ int? supervisorDeclarationValue,
+ string? supervisorCustomText,
+ int? leanerDeclarationValue,
+ string? leanerCustomText
+ );
+ void UpdateCompetencyAssessmentPublishStatus(int competencyAssessmentId, int status, int adminId);
+ void UpdateCompetencyAssessmentPublish(int competencyAssessmentId, int status, int adminId, bool national, bool pub);
+ void ArchiveSelfAssessmentReviewRequest(int reviewId);
+ void UpdateSelfAssessmentReview(int selfAssessmentID, int reviewId, bool signedOff, int? commentId);
+ void UpdateReviewRequestedDate(int reviewId);
+ bool UpdateCompetencyAssessmentReviewTaskStatus(int assessmentId, bool taskStatus);
+ //INSERT DATA
+ int InsertCompetencyAssessment(int adminId, int centreId, string competencyAssessmentName);
+ bool InsertSelfAssessmentFramework(int adminId, int selfAssessmentId, int frameworkId);
+ bool InsertCompetenciesIntoAssessmentFromFramework(int[] selectedCompetencyIds, int frameworkId, int competencyAssessmentId);
+ bool InsertSelfAssessmentGroupedCompetencies(int selfAssessmentId, int? frameworkId);
+ bool InsertSelfAssessmentUngroupedCompetencies(int selfAssessmentId, int? frameworkId);
+ int InsertAssessmentQuestionRoleRequirementForSelfAssessment(int assessmentId, int assessmentQuestionId, int levelValue, int? levelRAG);
+ int InsertCompetencyAssessmentQuestionRoleRequirement(int assessmentId, int competencyId, int assessmentQuestionId, int levelValue, int? levelRAG);
+ void InsertIntoSelfAssessmentCollaboratorsFromFrameworkCollaborators(int selfAssessmentId, int? frameworkId);
+ void InsertSelfAssessmentReview(int competencyAssessmentId, int selfAssessmentCollaboratorID, bool required);
+ int InsertComment(int selfAssessmentID, int adminId, string comment, int? replyToCommentId);
+ int InsertCompetencySelfAssessmentReview(int reviewId);
+
+ //DELETE DATA
+ bool RemoveFrameworkCompetenciesFromAssessment(int competencyAssessmentId, int frameworkId);
+ bool RemoveCompetencyFromAssessment(int competencyAssessmentId, int competencyId);
+ bool RemoveCompetencyGroupFromAssessment(int competencyAssessmentId, int competencyGroupId);
+ IEnumerable GetCollaboratorsForCompetencyAssessmentId(int competencyAssessmentId);
+ int AddCollaboratorToCompetencyAssessment(int competencyAssessmentId, string? userEmail, bool canModify, int? centreID);
+ void RemoveCollaboratorFromCompetencyAssessment(int competencyAssessmentId, int id);
+ CompetencyAssessmentCollaboratorNotification? GetCollaboratorNotification(int id, int invitedByAdminId);
+ bool HasCompetencyWithSignpostedLearning(int competencyAssessmentId);
+ bool DeleteCompetencyAssessmentQuestionRoleRequirement(int assessmentId, int? competencyId, int assessmentQuestionId, int levelValue);
+ }
+
+ public class CompetencyAssessmentDataService : ICompetencyAssessmentDataService
+ {
+ private const string SelfAssessmentBaseFields = @"sa.ID, sa.Name AS CompetencyAssessmentName, sa.Description, sa.BrandID,
+ sa.ParentSelfAssessmentID,
+ sa.[National], sa.[Public], sa.CreatedByAdminID AS OwnerAdminID,
+ sa.NRPProfessionalGroupID,
+ sa.NRPSubGroupID,
+ sa.NRPRoleID,
+ sa.PublishStatusID, sa.Vocabulary, CASE WHEN sa.CreatedByAdminID = @adminId THEN 3 WHEN sac.CanModify = 1 THEN 2 WHEN sac.CanModify = 0 THEN 1 ELSE 0 END AS UserRole,
+ sa.EnforceRoleRequirementsForSignOff, sa.IncludeRequirementsFilters,
+ sa.MinimumOptionalCompetencies,
+ sa.ManageOptionalCompetenciesPrompt,
+ sa.IncludeLearnerDeclarationPrompt, sa.IncludesSignposting, sa.LinearNavigation, sa.UseDescriptionExpanders, sa.QuestionLabel, sa.ReviewerCommentsLabel,
+ sa.SupervisorSelfAssessmentReview, sa.SupervisorResultsReview, sar.ID AS SelfAssessmentReviewID, sa.SignOffSupervisorStatement, sa.SignOffRequestorStatement,
+ sar.SelfAssessmentCommentID";
+
+ private const string SelfAssessmentFields =
+ @", sa.CategoryID, sa.CreatedDate,
+ (SELECT BrandName
+ FROM Brands
+ WHERE (BrandID = sa.BrandID)) AS Brand,
+ (SELECT CategoryName
+ FROM CourseCategories
+ WHERE (CourseCategoryID = sa.CategoryID)) AS Category,
+ (SELECT [Name]
+ FROM SelfAssessments AS sa2
+ WHERE (ID = sa.ParentSelfAssessmentID)) AS ParentSelfAssessment,
+ (SELECT Forename + ' ' + Surname + (CASE WHEN Active = 1 THEN '' ELSE ' (Inactive)' END) AS Expr1
+ FROM AdminUsers
+ WHERE (AdminID = sa.CreatedByAdminID)) AS Owner,
+ sa.Archived,
+ sa.LastEdit,
+ STUFF((
+ SELECT
+ ', ' + f.FrameworkName
+ FROM
+ SelfAssessmentFrameworks saf2
+ INNER JOIN Frameworks f ON f.ID = saf2.FrameworkId
+ WHERE
+ saf2.SelfAssessmentId = sa.ID AND saf2.RemovedDate IS NULL
+ FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 2, ''
+ ) AS LinkedFrameworks,
+ (SELECT ProfessionalGroup
+ FROM NRPProfessionalGroups
+ WHERE (ID = sa.NRPProfessionalGroupID)) AS NRPProfessionalGroup,
+ (SELECT SubGroup
+ FROM NRPSubGroups
+ WHERE (ID = sa.NRPSubGroupID)) AS NRPSubGroup,
+ (SELECT RoleProfile
+ FROM NRPRoles
+ WHERE (ID = sa.NRPRoleID)) AS NRPRole, sar.ID AS SelfAssessmentReviewID";
+
+ private const string SelfAssessmentBaseTables =
+ @"SelfAssessments AS sa LEFT OUTER JOIN
+ SelfAssessmentCollaborators AS sac ON sac.SelfAssessmentID = sa.ID AND sac.AdminID = @adminId";
+
+ private const string SelfAssessmentTables =
+ @" LEFT OUTER JOIN
+ SelfAssessmentReviews AS sar ON sac.ID = sar.SelfAssessmentCollaboratorID AND sar.Archived IS NULL AND sar.ReviewComplete IS NULL";
+
+ private readonly IDbConnection connection;
+ private readonly ILogger logger;
+
+ public CompetencyAssessmentDataService(IDbConnection connection, ILogger logger)
+ {
+ this.connection = connection;
+ this.logger = logger;
+ }
+
+ public IEnumerable GetAllCompetencyAssessments(int adminId)
+ {
+ return connection.Query(
+ $@"SELECT {SelfAssessmentBaseFields} {SelfAssessmentFields}
+ FROM {SelfAssessmentBaseTables} {SelfAssessmentTables}",
+ new { adminId }
+ );
+ }
+
+ public IEnumerable GetCompetencyAssessmentsForAdminId(int adminId)
+ {
+ return connection.Query(
+ $@"SELECT {SelfAssessmentBaseFields} {SelfAssessmentFields}
+ FROM {SelfAssessmentBaseTables} {SelfAssessmentTables}
+ WHERE (sa.CreatedByAdminID = @adminId) OR
+ (@adminId IN
+ (SELECT AdminID
+ FROM SelfAssessmentCollaborators
+ WHERE (SelfAssessmentID = sa.ID)))",
+ new { adminId }
+ );
+ }
+
+ public CompetencyAssessmentBase? GetCompetencyAssessmentBaseById(int competencyAssessmentId, int adminId)
+ {
+ return connection.Query(
+ $@"SELECT {SelfAssessmentBaseFields}
+ FROM {SelfAssessmentBaseTables} {SelfAssessmentTables}
+ WHERE (sa.ID = @competencyAssessmentId)",
+ new { competencyAssessmentId, adminId }
+ ).FirstOrDefault();
+ }
+
+ public int InsertCompetencyAssessment(int adminId, int centreId, string competencyAssessmentName)
+ {
+ if ((competencyAssessmentName.Length == 0) | (adminId < 1))
+ {
+ logger.LogWarning(
+ $"Not inserting competency assessmente as it failed server side validation. AdminId: {adminId}, competencyAssessmentName: {competencyAssessmentName}"
+ );
+ return -1;
+ }
+ var result = connection.ExecuteScalar(
+ @"SELECT COUNT(*) FROM SelfAssessments WHERE [Name] = @competencyAssessmentName",
+ new { competencyAssessmentName }
+ );
+ int existingSelfAssessments = Convert.ToInt32(result);
+ if (existingSelfAssessments > 0)
+ {
+ return -1;
+ }
+ var assessmentId = connection.QuerySingle(
+ @"INSERT INTO SelfAssessments ([Name], CreatedByCentreID, CreatedByAdminID)
+ OUTPUT INSERTED.Id
+ VALUES (@competencyAssessmentName, @centreId, @adminId)"
+ ,
+ new { competencyAssessmentName, centreId, adminId }
+ );
+ return assessmentId;
+ }
+ public bool UpdateCompetencyAssessmentName(int competencyAssessmentId, int adminId, string competencyAssessmentName)
+ {
+ if ((competencyAssessmentName.Length == 0) | (adminId < 1) | (competencyAssessmentId < 1))
+ {
+ logger.LogWarning(
+ $"Not updating role profile name as it failed server side validation. AdminId: {adminId}, competencyAssessmentName: {competencyAssessmentName}, competencyAssessmentId: {competencyAssessmentId}"
+ );
+ return false;
+ }
+ var result = connection.ExecuteScalar(
+ @"SELECT COUNT(*) FROM SelfAssessments WHERE [Name] = @competencyAssessmentName AND ID <> @competencyAssessmentId",
+ new { competencyAssessmentName, competencyAssessmentId }
+ );
+ int existingSelfAssessments = Convert.ToInt32(result);
+ if (existingSelfAssessments > 0)
+ {
+ return false;
+ }
+
+ var numberOfAffectedRows = connection.Execute(
+ @"UPDATE SelfAssessments SET [Name] = @competencyAssessmentName, UpdatedByAdminID = @adminId
+ WHERE ID = @competencyAssessmentId",
+ new { competencyAssessmentName, adminId, competencyAssessmentId }
+ );
+ if (numberOfAffectedRows < 1)
+ {
+ logger.LogWarning(
+ "Not updating role profile name as db update failed. " +
+ $"SelfAssessmentName: {competencyAssessmentName}, admin id: {adminId}, competencyAssessmentId: {competencyAssessmentId}"
+ );
+ return false;
+ }
+
+ return true;
+ }
+
+ public IEnumerable GetNRPProfessionalGroups()
+ {
+ return connection.Query(
+ @"SELECT ID, ProfessionalGroup, Active
+ FROM NRPProfessionalGroups
+ WHERE (Active = 1)
+ ORDER BY ProfessionalGroup"
+ );
+ }
+
+ public CompetencyAssessmentBase? GetCompetencyAssessmentBaseByName(string competencyAssessmentName, int adminId)
+ {
+ return connection.Query(
+ $@"SELECT {SelfAssessmentBaseFields}
+ FROM {SelfAssessmentBaseTables} {SelfAssessmentTables}
+ WHERE (sa.Name = @competencyAssessmentName)",
+ new { competencyAssessmentName, adminId }
+ ).FirstOrDefault();
+ }
+
+ public bool UpdateCompetencyRoleProfileLinks(int competencyAssessmentId, int adminId, int? professionalGroupId, int? subGroupId, int? roleId)
+ {
+ var result = connection.ExecuteScalar(
+ @"SELECT COUNT(*) FROM SelfAssessments WHERE ID = @competencyAssessmentId AND NRPProfessionalGroupID = @professionalGroupId AND NRPSubGroupID = @subGroupId AND NRPRoleID = @roleId",
+ new { competencyAssessmentId, professionalGroupId, subGroupId, roleId }
+ );
+ int sameCount = Convert.ToInt32(result);
+ if (sameCount > 0)
+ {
+ //same so don't update:
+ return false;
+ }
+
+ //needs updating:
+ var numberOfAffectedRows = connection.Execute(
+ @"UPDATE SelfAssessments SET NRPProfessionalGroupID = @professionalGroupId, NRPSubGroupID = @subGroupId, NRPRoleID = @roleId, UpdatedByAdminID = @adminId
+ WHERE ID = @competencyAssessmentId",
+ new { adminId, competencyAssessmentId, professionalGroupId, subGroupId, roleId }
+ );
+ if (numberOfAffectedRows > 0)
+ {
+ return true;
+ }
+
+ return false;
+ }
+ public bool UpdateCompetencyAssessmentBranding(
+ int competencyAssessmentId,
+ int adminId,
+ int brandId,
+ int categoryId
+ )
+ {
+ if ((competencyAssessmentId < 1) | (brandId < 1) | (categoryId < 1) | (adminId < 1))
+ {
+ logger.LogWarning(
+ $"Not updating competency assessment as it failed server side validation. competencyAssessmentId: {competencyAssessmentId}, brandId: {brandId}, categoryId: {categoryId}, AdminId: {adminId}"
+ );
+ return false;
+ }
+
+ var numberOfAffectedRows = connection.Execute(
+ @"UPDATE SelfAssessments SET BrandID = @brandId, CategoryID = @categoryId, UpdatedByAdminID = @adminId
+ WHERE ID = @competencyAssessmentId",
+ new { brandId, categoryId, adminId, competencyAssessmentId }
+ );
+ if (numberOfAffectedRows < 1)
+ {
+ logger.LogWarning(
+ "Not updating competency assessment branding as db update failed. " +
+ $"frameworkId: {competencyAssessmentId}, brandId: {brandId}, categoryId: {categoryId}, AdminId: {adminId}"
+ );
+ return false;
+ }
+
+ return true;
+ }
+
+ public bool UpdateCompetencyAssessmentDescription(int competencyAssessmentId, int adminId, string competencyAssessmentDescription)
+ {
+ var numberOfAffectedRows = connection.Execute(
+ @"UPDATE SelfAssessments SET Description = @competencyAssessmentDescription, UpdatedByAdminID = @adminId
+ WHERE ID = @competencyAssessmentId",
+ new { adminId, competencyAssessmentId, competencyAssessmentDescription }
+ );
+ if (numberOfAffectedRows < 1)
+ {
+ logger.LogWarning(
+ "Not updating competency assessment Description as db update failed. " +
+ $"frameworkId: {competencyAssessmentId}, competencyAssessmentDescription: {competencyAssessmentDescription}, AdminId: {adminId}"
+ );
+ return false;
+ }
+ return true;
+ }
+
+ public bool UpdateCompetencyAssessmentVocabulary(int competencyAssessmentId, int adminId, string vocabulary)
+ {
+ var numberOfAffectedRows = connection.Execute(
+ @"UPDATE SelfAssessments SET Vocabulary = @vocabulary, UpdatedByAdminID = @adminId
+ WHERE ID = @competencyAssessmentId",
+ new { adminId, competencyAssessmentId, vocabulary }
+ );
+ if (numberOfAffectedRows < 1)
+ {
+ logger.LogWarning(
+ "Not updating competency assessment vocabulary as db update failed. " +
+ $"frameworkId: {competencyAssessmentId}, vocabulary: {vocabulary}, AdminId: {adminId}"
+ );
+ return false;
+ }
+ return true;
+ }
+
+ public bool InsertSelfAssessmentFramework(int adminId, int selfAssessmentId, int frameworkId)
+ {
+ bool isPrimary = Convert.ToInt32(connection.ExecuteScalar(
+ @"SELECT Count(1) FROM SelfAssessmentFrameworks
+ WHERE SelfAssessmentId = @selfAssessmentId AND IsPrimary = 1", new { selfAssessmentId })) == 0;
+
+ var numberOfAffectedRows = connection.Execute(
+ @"INSERT INTO SelfAssessmentFrameworks (SelfAssessmentId, FrameworkId, CreatedByAdminId, IsPrimary)
+ SELECT @selfAssessmentId, @frameworkId, @adminId, @isPrimary
+ WHERE NOT EXISTS (SELECT 1 FROM SelfAssessmentFrameworks WHERE SelfAssessmentId = @selfAssessmentId AND FrameworkId = @frameworkId)"
+ ,
+ new { adminId, selfAssessmentId, frameworkId, isPrimary }
+ );
+ if (numberOfAffectedRows < 1)
+ {
+ numberOfAffectedRows = connection.Execute(
+ @"UPDATE SelfAssessmentFrameworks
+ SET RemovedDate = NULL, RemovedByAdminId = NULL, AmendedByAdminId = @adminId, IsPrimary = 0
+ WHERE SelfAssessmentId = @selfAssessmentId AND FrameworkId = @frameworkId"
+ ,
+ new { adminId, selfAssessmentId, frameworkId }
+ );
+ }
+ if (numberOfAffectedRows < 1)
+ {
+ logger.LogWarning(
+ "Not inserting SelfAssessmentFrameworks record as db insert failed. " +
+ $"selfAssessmentId: {selfAssessmentId}, frameworkId: {frameworkId}, AdminId: {adminId}"
+ );
+ return false;
+ }
+
+ return true;
+ }
+
+ public CompetencyAssessmentTaskStatus GetOrInsertAndReturnAssessmentTaskStatus(int assessmentId, bool frameworkBased)
+ {
+ bool? frameworkItemBool = frameworkBased ? false : null;
+ connection.Execute(
+ @"INSERT INTO SelfAssessmentTaskStatus (SelfAssessmentId, IntroductoryTextTaskStatus, BrandingTaskStatus, VocabularyTaskStatus, FrameworkLinksTaskStatus)
+ SELECT @assessmentId, @frameworkItemBool, @frameworkItemBool, @frameworkItemBool, @frameworkItemBool
+ WHERE NOT EXISTS (SELECT 1 FROM SelfAssessmentTaskStatus WHERE SelfAssessmentId = @assessmentId)", new { assessmentId, frameworkItemBool });
+ return connection.Query(
+ $@"SELECT *
+ FROM SelfAssessmentTaskStatus
+ WHERE (SelfAssessmentId = @assessmentId)",
+ new { assessmentId }
+ ).Single();
+
+ }
+ public bool UpdateIntroductoryTextTaskStatus(int assessmentId, bool taskStatus)
+ {
+ var numberOfAffectedRows = connection.Execute(
+ @"UPDATE SelfAssessmentTaskStatus SET IntroductoryTextTaskStatus = @taskStatus
+ WHERE SelfAssessmentId = @assessmentId",
+ new { assessmentId, taskStatus }
+ );
+ if (numberOfAffectedRows < 1)
+ {
+ logger.LogWarning(
+ "Not updating IntroductoryTextTaskStatus as db update failed. " +
+ $"assessmentId: {assessmentId}, taskStatus: {taskStatus}"
+ );
+ return false;
+ }
+ return true;
+ }
+
+ public CompetencyAssessment? GetCompetencyAssessmentById(int competencyAssessmentId, int adminId)
+ {
+ return connection.Query(
+ $@"SELECT {SelfAssessmentBaseFields} {SelfAssessmentFields}
+ FROM {SelfAssessmentBaseTables} {SelfAssessmentTables}
+ WHERE (sa.ID = @competencyAssessmentId)",
+ new { competencyAssessmentId, adminId }
+ ).FirstOrDefault();
+ }
+
+ public bool UpdateBrandingTaskStatus(int assessmentId, bool taskStatus)
+ {
+ var numberOfAffectedRows = connection.Execute(
+ @"UPDATE SelfAssessmentTaskStatus SET BrandingTaskStatus = @taskStatus
+ WHERE SelfAssessmentId = @assessmentId",
+ new { assessmentId, taskStatus }
+ );
+ if (numberOfAffectedRows < 1)
+ {
+ logger.LogWarning(
+ "Not updating BrandingTaskStatus as db update failed. " +
+ $"assessmentId: {assessmentId}, taskStatus: {taskStatus}"
+ );
+ return false;
+ }
+ return true;
+ }
+
+ public bool UpdateVocabularyTaskStatus(int assessmentId, bool taskStatus)
+ {
+ var numberOfAffectedRows = connection.Execute(
+ @"UPDATE SelfAssessmentTaskStatus SET VocabularyTaskStatus = @taskStatus
+ WHERE SelfAssessmentId = @assessmentId",
+ new { assessmentId, taskStatus }
+ );
+ if (numberOfAffectedRows < 1)
+ {
+ logger.LogWarning(
+ "Not updating VocabularyTaskStatus as db update failed. " +
+ $"assessmentId: {assessmentId}, taskStatus: {taskStatus}"
+ );
+ return false;
+ }
+ return true;
+ }
+
+ public IEnumerable GetNRPSubGroups(int? nRPProfessionalGroupID)
+ {
+ return connection.Query(
+ @"SELECT ID, SubGroup, Active
+ FROM NRPSubGroups
+ WHERE (Active = 1) AND (NRPProfessionalGroupID = @nRPProfessionalGroupID)
+ ORDER BY SubGroup", new { nRPProfessionalGroupID }
+ );
+ }
+
+ public IEnumerable GetNRPRoles(int? nRPSubGroupID)
+ {
+ return connection.Query(
+ @"SELECT ID, RoleProfile AS ProfileName, Active
+ FROM NRPRoles
+ WHERE (Active = 1) AND (NRPSubGroupID = @nRPSubGroupID)
+ ORDER BY RoleProfile", new { nRPSubGroupID }
+ );
+ }
+
+ public bool UpdateRoleProfileLinksTaskStatus(int assessmentId, bool taskStatus)
+ {
+ var numberOfAffectedRows = connection.Execute(
+ @"UPDATE SelfAssessmentTaskStatus SET NationalRoleProfileTaskStatus = @taskStatus
+ WHERE SelfAssessmentId = @assessmentId",
+ new { assessmentId, taskStatus }
+ );
+ if (numberOfAffectedRows < 1)
+ {
+ logger.LogWarning(
+ "Not updating NationalRoleProfileTaskStatus as db update failed. " +
+ $"assessmentId: {assessmentId}, taskStatus: {taskStatus}"
+ );
+ return false;
+ }
+ return true;
+ }
+
+ public int[] GetLinkedFrameworkIds(int assessmentId)
+ {
+ return [.. connection.Query(
+ @"SELECT FrameworkId
+ FROM SelfAssessmentFrameworks
+ WHERE (SelfAssessmentId = @assessmentId) AND (RemovedDate IS NULL) AND (IsPrimary = 0)
+ ORDER BY ID",
+ new { assessmentId }
+ )];
+ }
+
+ public bool RemoveSelfAssessmentFramework(int assessmentId, int frameworkId, int adminId)
+ {
+ var numberOfAffectedRows = connection.Execute(
+ @"UPDATE SelfAssessmentFrameworks SET RemovedDate = @removedDate, RemovedByAdminId = @adminId
+ WHERE SelfAssessmentId = @assessmentId AND FrameworkId = @frameworkId",
+ new { removedDate = DateTime.Now, assessmentId, frameworkId, adminId }
+ );
+ if (numberOfAffectedRows < 1)
+ {
+ logger.LogWarning(
+ "Not updating SelfAssessmentFrameworks as db update failed. " +
+ $"assessmentId: {assessmentId}, frameworkId: {frameworkId}, adminId: {adminId}"
+ );
+ return false;
+ }
+ return true;
+ }
+
+ public int? GetPrimaryLinkedFrameworkId(int assessmentId)
+ {
+ return connection.QuerySingleOrDefault(
+ @"SELECT TOP(1) FrameworkId
+ FROM SelfAssessmentFrameworks
+ WHERE (SelfAssessmentId = @assessmentId) AND (RemovedDate IS NULL) AND (IsPrimary = 1)
+ ORDER BY ID DESC",
+ new { assessmentId }
+ );
+ }
+
+ public bool UpdateFrameworkLinksTaskStatus(int assessmentId, bool taskStatus, bool? previousStatus)
+ {
+ var numberOfAffectedRows = connection.Execute(
+ @"UPDATE SelfAssessmentTaskStatus SET FrameworkLinksTaskStatus = @taskStatus
+ WHERE SelfAssessmentId = @assessmentId AND (@previousStatus IS NULL OR FrameworkLinksTaskStatus = @previousStatus)",
+ new { assessmentId, taskStatus, previousStatus }
+ );
+ if (numberOfAffectedRows < 1)
+ {
+ logger.LogWarning(
+ "Not updating FrameworkLinksTaskStatus as db update failed. " +
+ $"assessmentId: {assessmentId}, taskStatus: {taskStatus}"
+ );
+ return false;
+ }
+ return true;
+ }
+
+ public int GetCompetencyCountByFrameworkId(int assessmentId, int frameworkId)
+ {
+ return connection.ExecuteScalar(
+ @"SELECT COUNT(sas.CompetencyID) AS Competencies
+ FROM SelfAssessmentStructure AS sas INNER JOIN
+ FrameworkCompetencies AS fc ON sas.CompetencyID = fc.CompetencyID INNER JOIN
+ SelfAssessmentFrameworks AS saf ON fc.FrameworkID = saf.FrameworkId AND sas.SelfAssessmentID = saf.SelfAssessmentId
+ WHERE (saf.SelfAssessmentId = @assessmentId) AND (saf.FrameworkId = @frameworkId)",
+ new { assessmentId, frameworkId }
+ );
+ }
+
+ public bool RemoveFrameworkCompetenciesFromAssessment(int competencyAssessmentId, int frameworkId)
+ {
+ var numberOfAffectedRows = connection.Execute(
+ @"DELETE FROM SelfAssessmentStructure
+ FROM SelfAssessmentStructure INNER JOIN
+ FrameworkCompetencies AS fc ON SelfAssessmentStructure.CompetencyID = fc.CompetencyID INNER JOIN
+ SelfAssessmentFrameworks AS saf ON fc.FrameworkID = saf.FrameworkId AND SelfAssessmentStructure.SelfAssessmentID = saf.SelfAssessmentId
+ WHERE (saf.SelfAssessmentId = @competencyAssessmentId) AND (saf.FrameworkId = @frameworkId)",
+ new { competencyAssessmentId, frameworkId }
+ );
+ if (numberOfAffectedRows < 1)
+ {
+ logger.LogWarning(
+ "Not removing competencies linked to source framework as db update failed. " +
+ $"assessmentId: {competencyAssessmentId}, taskStatus: {frameworkId}"
+ );
+ return false;
+ }
+ return true;
+ }
+
+ public bool UpdateSelectCompetenciesTaskStatus(int assessmentId, bool taskStatus, bool? previousStatus)
+ {
+ var numberOfAffectedRows = connection.Execute(
+ @"UPDATE SelfAssessmentTaskStatus SET SelectCompetenciesTaskStatus = @taskStatus
+ WHERE SelfAssessmentId = @assessmentId AND (@previousStatus IS NULL OR SelectCompetenciesTaskStatus = @previousStatus)",
+ new { assessmentId, taskStatus, previousStatus }
+ );
+ if (numberOfAffectedRows < 1)
+ {
+ logger.LogWarning(
+ "Not updating SelectCompetenciesTaskStatus as db update failed. " +
+ $"assessmentId: {assessmentId}, taskStatus: {taskStatus}"
+ );
+ return false;
+ }
+ return true;
+ }
+ public bool UpdateOptionalCompetenciesTaskStatus(int assessmentId, bool taskStatus, bool? previousStatus)
+ {
+ var numberOfAffectedRows = connection.Execute(
+ @"UPDATE SelfAssessmentTaskStatus SET OptionalCompetenciesTaskStatus = @taskStatus
+ WHERE SelfAssessmentId = @assessmentId AND (@previousStatus IS NULL OR OptionalCompetenciesTaskStatus = @previousStatus)",
+ new { assessmentId, taskStatus, previousStatus }
+ );
+ if (numberOfAffectedRows < 1)
+ {
+ logger.LogWarning(
+ "Not updating OptionalCompetenciesTaskStatus as db update failed. " +
+ $"assessmentId: {assessmentId}, taskStatus: {taskStatus}"
+ );
+ return false;
+ }
+ return true;
+ }
+ public bool UpdateRoleRequirementsTaskStatus(int assessmentId, bool taskStatus, bool? previousStatus)
+ {
+ var numberOfAffectedRows = connection.Execute(
+ @"UPDATE SelfAssessmentTaskStatus SET RoleRequirementsTaskStatus = @taskStatus
+ WHERE SelfAssessmentId = @assessmentId AND (@previousStatus IS NULL OR RoleRequirementsTaskStatus = @previousStatus)",
+ new { assessmentId, taskStatus, previousStatus }
+ );
+ if (numberOfAffectedRows < 1)
+ {
+ logger.LogWarning(
+ "Not updating RoleRequirementsTaskStatus as db update failed. " +
+ $"assessmentId: {assessmentId}, taskStatus: {taskStatus}"
+ );
+ return false;
+ }
+ return true;
+ }
+ public bool UpdateWorkingGroupTaskStatus(int assessmentId, bool taskStatus, bool? previousStatus)
+ {
+ var numberOfAffectedRows = connection.Execute(
+ @"UPDATE SelfAssessmentTaskStatus SET WorkingGroupTaskStatus = @taskStatus
+ WHERE SelfAssessmentId = @assessmentId AND (@previousStatus IS NULL OR WorkingGroupTaskStatus = @previousStatus)",
+ new { assessmentId, taskStatus, previousStatus }
+ );
+ if (numberOfAffectedRows < 1)
+ {
+ logger.LogWarning(
+ "Not updating WorkingGroupTaskStatus as db update failed. " +
+ $"assessmentId: {assessmentId}, taskStatus: {taskStatus}"
+ );
+ return false;
+ }
+ return true;
+ }
+
+ public IEnumerable GetCompetenciesForCompetencyAssessment(int competencyAssessmentId)
+ {
+ return connection.Query(
+ @"SELECT sas.ID AS StructureId, sas.CompetencyID, f.ID AS FrameworkId, f.FrameworkName, cg.ID AS GroupId, cg.Name AS GroupName, c.Name AS CompetencyName, c.Description AS CompetencyDescription, sas.Optional, sas.GroupOptionalCompetencies
+ FROM SelfAssessmentStructure AS sas INNER JOIN
+ Competencies AS c ON sas.CompetencyID = c.ID LEFT JOIN
+ CompetencyGroups AS cg ON sas.CompetencyGroupID = cg.ID INNER JOIN
+ FrameworkCompetencies ON c.ID = FrameworkCompetencies.CompetencyID INNER JOIN
+ Frameworks AS f ON FrameworkCompetencies.FrameworkID = f.ID INNER JOIN
+ SelfAssessmentFrameworks ON f.ID = SelfAssessmentFrameworks.FrameworkId AND sas.SelfAssessmentID = SelfAssessmentFrameworks.SelfAssessmentId
+ WHERE (sas.SelfAssessmentID = @competencyAssessmentId)
+ ORDER BY sas.Ordering", new { competencyAssessmentId }
+ );
+ }
+
+ public IEnumerable GetLinkedFrameworksForCompetencyAssessment(int competencyAssessmentId)
+ {
+ return connection.Query(
+ @"SELECT f.ID,
+ FrameworkName,
+ f.OwnerAdminID,
+ f.BrandID,
+ f.CategoryID,
+ f.TopicID,
+ f.CreatedDate,
+ f.PublishStatusID,
+ f.UpdatedByAdminID,
+ saf.IsPrimary
+ FROM SelfAssessmentFrameworks saf INNER JOIN
+ Frameworks AS f ON saf.FrameworkId = f.ID
+ WHERE (saf.SelfAssessmentId = @competencyAssessmentId) AND (saf.RemovedDate IS NULL)
+ ORDER BY saf.ID", new { competencyAssessmentId }
+ );
+ }
+
+ public int[] GetLinkedFrameworkCompetencyIds(int competencyAssessmentId, int frameworkId)
+ {
+ return [.. connection.Query(
+ @"SELECT sas.CompetencyID
+ FROM SelfAssessmentStructure AS sas INNER JOIN
+ FrameworkCompetencies AS fc ON sas.CompetencyID = fc.CompetencyID
+ WHERE (sas.SelfAssessmentID = @competencyAssessmentId) AND (fc.FrameworkID = @frameworkId)
+ ORDER BY fc.Ordering",
+ new { competencyAssessmentId, frameworkId}
+ )];
+ }
+
+ public bool InsertCompetenciesIntoAssessmentFromFramework(int[] selectedCompetencyIds, int frameworkId, int competencyAssessmentId)
+ {
+
+ var currentMaxOrdering = connection.ExecuteScalar(
+ @"SELECT ISNULL(MAX(Ordering), 0) FROM SelfAssessmentStructure WHERE SelfAssessmentID = @competencyAssessmentId",
+ new { competencyAssessmentId }
+ );
+ var numberOfAffectedRows = connection.Execute(
+ @"INSERT INTO SelfAssessmentStructure (SelfAssessmentID, CompetencyID, Ordering, CompetencyGroupID)
+ SELECT
+ @competencyAssessmentId,
+ FC.CompetencyID,
+ ROW_NUMBER() OVER (ORDER BY FCG.Ordering, FC.Ordering) + @currentMaxOrdering,
+ FCG.CompetencyGroupID
+ FROM FrameworkCompetencies AS FC
+ LEFT JOIN FrameworkCompetencyGroups AS FCG ON FC.FrameworkCompetencyGroupID = FCG.ID
+ WHERE FC.FrameworkID = @frameworkId
+ AND FC.CompetencyID IN @selectedCompetencyIds AND FC.CompetencyID NOT IN (SELECT CompetencyID FROM SelfAssessmentStructure WHERE SelfAssessmentID = @competencyAssessmentId)",
+ new { selectedCompetencyIds, frameworkId, competencyAssessmentId, currentMaxOrdering }
+ );
+ if (numberOfAffectedRows < 1)
+ {
+ logger.LogWarning(
+ "Not inserting competencies into assessment as db update failed. " +
+ $"assessmentId: {competencyAssessmentId}, frameworkId: {frameworkId}, selectedCompetencyIds: {selectedCompetencyIds}"
+ );
+ return false;
+ }
+ return true;
+ }
+ public bool RemoveCompetencyFromAssessment(int competencyAssessmentId, int competencyId)
+ {
+ var numberOfAffectedRows = connection.Execute(
+ @"DELETE FROM SelfAssessmentStructure
+ WHERE SelfAssessmentID = @competencyAssessmentId AND CompetencyID = @competencyId",
+ new { competencyAssessmentId, competencyId }
+ );
+ if (numberOfAffectedRows < 1)
+ {
+ logger.LogWarning(
+ "Not removing competency from assessment as db update failed. " +
+ $"assessmentId: {competencyAssessmentId}, competencyId: {competencyId}"
+ );
+ return false;
+ }
+ return true;
+ }
+
+ public bool RemoveCompetencyGroupFromAssessment(int competencyAssessmentId, int competencyGroupId)
+ {
+ var numberOfAffectedRows = connection.Execute(
+ @"DELETE FROM SelfAssessmentStructure
+ WHERE SelfAssessmentID = @competencyAssessmentId AND CompetencyGroupId = @competencyGroupId",
+ new { competencyAssessmentId, competencyGroupId }
+ );
+ if (numberOfAffectedRows < 1)
+ {
+ logger.LogWarning(
+ "Not removing competency from assessment as db update failed. " +
+ $"assessmentId: {competencyAssessmentId}, competencyGroupId: {competencyGroupId}"
+ );
+ return false;
+ }
+ return true;
+ }
+
+ public void MoveCompetencyInSelfAssessment(int competencyAssessmentId, int competencyId, string direction)
+ {
+ connection.Execute(
+ "usp_MoveCompetencyInSelfAssessment",
+ new { SelfAssessmentID = competencyAssessmentId, CompetencyID = competencyId, Direction = direction },
+ commandType: CommandType.StoredProcedure
+ );
+
+ }
+
+ public void MoveCompetencyGroupInSelfAssessment(int competencyAssessmentId, int groupId, string direction)
+ {
+ connection.Execute(
+ "usp_MoveCompetencyGroupInSelfAssessment",
+ new { SelfAssessmentID = competencyAssessmentId, GroupID = groupId, Direction = direction },
+ commandType: CommandType.StoredProcedure
+ );
+ }
+
+ public bool UpdateCompetencyAssessmentFeaturesTaskStatus(int id, bool descriptionStatus, bool providerandCategoryStatus, bool vocabularyStatus,
+ bool workingGroupStatus, bool AllframeworkCompetenciesStatus)
+ {
+ var numberOfAffectedRows = connection.Execute(
+ @"IF EXISTS (SELECT 1 FROM SelfAssessmentTaskStatus WHERE SelfAssessmentId = @id)
+ BEGIN
+ UPDATE SelfAssessmentTaskStatus
+ SET IntroductoryTextTaskStatus =
+ CASE WHEN @descriptionStatus = 1 AND IntroductoryTextTaskStatus <> 1
+ THEN 0 ELSE IntroductoryTextTaskStatus END,
+ BrandingTaskStatus =
+ CASE WHEN @providerandCategoryStatus = 1 AND BrandingTaskStatus <> 1
+ THEN 0 ELSE BrandingTaskStatus END,
+ VocabularyTaskStatus =
+ CASE WHEN @vocabularyStatus = 1 AND VocabularyTaskStatus <> 1
+ THEN 0 ELSE VocabularyTaskStatus END,
+ WorkingGroupTaskStatus =
+ CASE WHEN @workingGroupStatus = 1 AND WorkingGroupTaskStatus <> 1
+ THEN 0 ELSE WorkingGroupTaskStatus END,
+ FrameworkLinksTaskStatus =
+ CASE WHEN @AllframeworkCompetenciesStatus = 1 AND FrameworkLinksTaskStatus <> 1
+ THEN 0 ELSE FrameworkLinksTaskStatus END,
+ SelectCompetenciesTaskStatus =
+ CASE WHEN @AllframeworkCompetenciesStatus = 1 AND SelectCompetenciesTaskStatus <> 1
+ THEN 0 ELSE SelectCompetenciesTaskStatus END
+ WHERE SelfAssessmentId = @id;
+ END
+ ELSE
+ BEGIN
+ INSERT INTO SelfAssessmentTaskStatus
+ (SelfAssessmentId, IntroductoryTextTaskStatus, BrandingTaskStatus, VocabularyTaskStatus, WorkingGroupTaskStatus,
+ FrameworkLinksTaskStatus,SelectCompetenciesTaskStatus)
+ VALUES
+ (
+ @id,
+ CASE WHEN @descriptionStatus = 1 THEN 0 ELSE NULL END,
+ CASE WHEN @providerandCategoryStatus = 1 THEN 0 ELSE NULL END,
+ CASE WHEN @vocabularyStatus = 1 THEN 0 ELSE NULL END,
+ CASE WHEN @workingGroupStatus = 1 THEN 0 ELSE NULL END,
+ CASE WHEN @AllframeworkCompetenciesStatus = 1 THEN 0 ELSE NULL END,
+ CASE WHEN @AllframeworkCompetenciesStatus = 1 THEN 0 ELSE NULL END
+ );
+ END",
+ new { id, descriptionStatus, providerandCategoryStatus, vocabularyStatus, workingGroupStatus, AllframeworkCompetenciesStatus }
+ );
+ if (numberOfAffectedRows < 1)
+ {
+ logger.LogWarning(
+ "Not updating SelfAssessmentTaskStatus as db update failed. " +
+ $"SelfAssessmentId: {id}, IntroductoryTextTaskStatus: {descriptionStatus}, BrandingTaskStatus: {providerandCategoryStatus}, " +
+ $"VocabularyTaskStatus: {vocabularyStatus}, WorkingGroupTaskStatus: {workingGroupStatus}, FrameworkLinksTaskStatus: {AllframeworkCompetenciesStatus}"
+ );
+ return false;
+ }
+ return true;
+ }
+
+ public CompetencyAssessmentFeatures? GetCompetencyAssessmentFeaturesTaskStatus(int competencyAssessmentId)
+ {
+ return connection.QueryFirstOrDefault(
+ @"SELECT s.ID, s.Name AS CompetencyAssessmentName, sts.IntroductoryTextTaskStatus AS DescriptionStatus, sts.BrandingTaskStatus AS ProviderandCategoryStatus,
+ sts.VocabularyTaskStatus AS VocabularyStatus, sts.WorkingGroupTaskStatus AS WorkingGroupStatus, sts.FrameworkLinksTaskStatus AS AllframeworkCompetenciesStatus
+ FROM SelfAssessments s INNER JOIN
+ SelfAssessmentTaskStatus sts ON s.ID = sts.SelfAssessmentId
+ WHERE s.ID = @competencyAssessmentId",
+ new { competencyAssessmentId }
+ );
+
+ }
+
+ public void UpdateSelfAssessmentFromFramework(int selfAssessmentId, int? frameworkId)
+ {
+
+ var numberOfAffectedRows = connection.Execute(
+ @"UPDATE s
+ SET
+ [Description] = CASE
+ WHEN sts.IntroductoryTextTaskStatus IS NULL THEN NULL
+ ELSE COALESCE(F.[Description], 'No description provided')
+ END,
+ BrandID = CASE
+ WHEN sts.BrandingTaskStatus IS NULL THEN s.BrandID
+ ELSE F.BrandID
+ END,
+ CategoryID = CASE
+ WHEN sts.BrandingTaskStatus IS NULL THEN s.CategoryID
+ ELSE F.CategoryID
+ END,
+ Vocabulary = CASE
+ WHEN sts.VocabularyTaskStatus IS NULL THEN NULL
+ ELSE F.FrameworkConfig
+ END
+ FROM SelfAssessments s
+ INNER JOIN Frameworks F ON F.ID = @frameworkId
+ INNER JOIN AdminUsers AU ON F.OwnerAdminID = AU.AdminID
+ INNER JOIN SelfAssessmentTaskStatus sts ON s.ID = sts.SelfAssessmentId
+ WHERE s.ID = @selfAssessmentId;",
+ new { selfAssessmentId, frameworkId }
+ );
+ }
+ public bool InsertSelfAssessmentGroupedCompetencies(int selfAssessmentId, int? frameworkId)
+ {
+
+ var numberOfAffectedRows = connection.Execute(
+ @"INSERT INTO SelfAssessmentStructure (SelfAssessmentID, CompetencyID, Ordering, CompetencyGroupID)
+ SELECT s.ID, FC.CompetencyID, ROW_NUMBER() OVER( ORDER BY FCG.Ordering, FC.Ordering ), FCG.CompetencyGroupID
+ FROM FrameworkCompetencies AS FC
+ INNER JOIN FrameworkCompetencyGroups AS FCG ON FC.FrameworkCompetencyGroupID = FCG.ID INNER JOIN
+ SelfAssessments s ON s.id = @selfAssessmentId
+ WHERE FC.FrameworkID = @frameworkId"
+ ,
+ new { selfAssessmentId, frameworkId }
+ );
+ if (numberOfAffectedRows < 1)
+ {
+ logger.LogWarning(
+ "Not inserting SelfAssessmentStructure record as db insert failed. " +
+ $"selfAssessmentId: {selfAssessmentId}, frameworkId: {frameworkId}"
+ );
+ return false;
+ }
+
+ return true;
+ }
+ public bool InsertSelfAssessmentUngroupedCompetencies(int selfAssessmentId, int? frameworkId)
+ {
+
+ var numberOfAffectedRows = connection.Execute(
+ @"INSERT INTO SelfAssessmentStructure (SelfAssessmentID, CompetencyID, Ordering)
+ SELECT s.ID, FC.CompetencyID, FC.Ordering
+ FROM FrameworkCompetencies AS fc
+ INNER JOIN SelfAssessments s ON s.id = @selfAssessmentId
+ WHERE fc.FrameworkID = @frameworkId
+ AND fc.FrameworkCompetencyGroupID IS NULL "
+ ,
+ new { selfAssessmentId, frameworkId }
+ );
+ if (numberOfAffectedRows < 1)
+ {
+ logger.LogWarning(
+ "Not inserting SelfAssessmentStructure record as db insert failed. " +
+ $"selfAssessmentId: {selfAssessmentId}, frameworkId: {frameworkId}"
+ );
+ return false;
+ }
+
+ return true;
+ }
+
+ public bool UpdatePrimaryFrameworkCompetencies(int assessmentId, int frameworkId)
+ {
+ connection.EnsureOpen();
+ using (var transaction = connection.BeginTransaction())
+ {
+ var numberOfAffectedRows = connection.Execute(
+ @"UPDATE SelfAssessmentFrameworks
+ SET IsPrimary = 0
+ WHERE (SelfAssessmentId = @assessmentId)
+ AND (RemovedDate IS NULL)",
+ new { assessmentId },
+ transaction: transaction
+ );
+
+ var numberOfAffectedRow = connection.Execute(
+ @"UPDATE SelfAssessmentFrameworks
+ SET IsPrimary = 1
+ WHERE (SelfAssessmentId = @assessmentId)
+ AND (FrameworkId = @frameworkId)
+ AND (RemovedDate IS NULL)",
+ new { assessmentId, frameworkId },
+ transaction: transaction
+ );
+
+ if ((numberOfAffectedRow < 1) || (numberOfAffectedRows < 1))
+ {
+ logger.LogWarning(
+ "Not updating SelfAssessmentFrameworks as db update failed. " +
+ $"assessmentId: {assessmentId}, frameworkId: {frameworkId}"
+ );
+ transaction.Rollback();
+ return false;
+ }
+
+ transaction.Commit();
+ return true;
+ }
+ }
+ public int? GetSelfAssessmentStructure(int competencyAssessmentId)
+ {
+ return connection.QueryFirstOrDefault(
+ @"SELECT 1 from dbo.SelfAssessmentStructure where selfassessmentid = @competencyAssessmentId",
+ new { competencyAssessmentId }
+ );
+ }
+ public IEnumerable GetCollaboratorsForCompetencyAssessmentId(int competencyAssessmentId)
+ {
+ return connection.Query(
+ @"SELECT
+ 0 AS ID,
+ sa.ID AS SelfAssessmentID,
+ au.AdminID AS AdminID,
+ 1 AS CanModify,
+ au.Email AS UserEmail,
+ au.Active AS UserActive,
+ 'Owner' AS CompetencyAssessmentRole,
+ 0 AS SelfAssessmentReviewID,
+ 0 AS SignOffRequired
+ FROM SelfAssessments AS sa
+ INNER JOIN AdminUsers AS au ON sa.CreatedByAdminID = au.AdminID
+ WHERE (sa.ID = @competencyAssessmentId)
+ UNION ALL
+ SELECT
+ sc.ID,
+ sc.SelfAssessmentID,
+ sc.AdminID,
+ CanModify,
+ UserEmail,
+ au.Active AS UserActive,
+ CASE WHEN CanModify = 1 THEN 'Contributor' ELSE 'Reviewer' END AS CompetencyAssessmentRole,
+ sr.ID AS SelfAssessmentReviewID,
+ sr.SignOffRequired
+ FROM SelfAssessmentCollaborators sc
+ INNER JOIN AdminUsers AS au ON sc.AdminID = au.AdminID
+ AND sc.IsDeleted = 0
+ LEFT OUTER JOIN SelfAssessmentReviews sr ON sr.SelfAssessmentCollaboratorID = sc.ID AND sr.Archived IS NULL
+ WHERE (sc.SelfAssessmentID = @competencyAssessmentId)",
+ new { competencyAssessmentId }
+ );
+ }
+ public int AddCollaboratorToCompetencyAssessment(int competencyAssessmentId, string? userEmail, bool canModify, int? centreID)
+ {
+ if (userEmail is null || userEmail.Length == 0)
+ {
+ logger.LogWarning(
+ $"Not adding collaborator to competency assessment as it failed server side valiidation. competencyAssessmentId: {competencyAssessmentId}, userEmail: {userEmail}, canModify:{canModify}"
+ );
+ return -3;
+ }
+
+ var existingId = connection.QuerySingle(
+ @"SELECT COALESCE
+ ((SELECT ID
+ FROM SelfAssessmentCollaborators
+ WHERE (SelfAssessmentID = @competencyAssessmentId) AND (UserEmail = @userEmail) AND (IsDeleted=0)), 0) AS ID",
+ new { competencyAssessmentId, userEmail }
+ );
+ if (existingId > 0)
+ {
+ return -2;
+ }
+
+ var adminId = (int?)connection.ExecuteScalar(
+ @"SELECT AdminID FROM AdminUsers WHERE Email = @userEmail AND Active = 1 AND CentreID = @centreID",
+ new { userEmail, centreID }
+ );
+ if (adminId is null)
+ {
+ return -4;
+ }
+
+ var ownerEmail = (string?)connection.ExecuteScalar(@"SELECT AU.Email FROM SelfAssessments SA
+ INNER JOIN AdminUsers AU ON AU.AdminID = SA.CreatedByAdminID
+ WHERE SA.ID = @competencyAssessmentId", new { competencyAssessmentId });
+ if (ownerEmail == userEmail)
+ {
+ return -5;
+ }
+
+ var numberOfAffectedRows = connection.Execute(
+ @"INSERT INTO SelfAssessmentCollaborators (SelfAssessmentID, AdminID, UserEmail, CanModify)
+ VALUES (@competencyAssessmentId, @adminId, @userEmail, @canModify)",
+ new { competencyAssessmentId, adminId, userEmail, canModify }
+ );
+ if (numberOfAffectedRows < 1)
+ {
+ logger.LogWarning(
+ $"Not inserting framework collaborator as db insert failed. AdminId: {adminId}, userEmail: {userEmail}, competencyAssessmentId: {competencyAssessmentId}, canModify: {canModify}"
+ );
+ return -1;
+ }
+
+ if (adminId > 0)
+ {
+ connection.Execute(
+ @"UPDATE AdminUsers SET IsWorkforceManager = 1 WHERE AdminId = @adminId AND IsWorkforceManager = 0",
+ new { adminId }
+ );
+ }
+
+ existingId = connection.QuerySingle(
+ @"SELECT COALESCE
+ ((SELECT ID
+ FROM SelfAssessmentCollaborators
+ WHERE (SelfAssessmentID = @competencyAssessmentId) AND (UserEmail = @userEmail) AND (IsDeleted=0)), 0) AS AdminID",
+ new { competencyAssessmentId, adminId, userEmail }
+ );
+ return existingId;
+ }
+
+ public void RemoveCollaboratorFromCompetencyAssessment(int competencyAssessmentId, int id)
+ {
+ var adminId = (int?)connection.ExecuteScalar(
+ @"SELECT AdminID FROM SelfAssessmentCollaborators WHERE (SelfAssessmentID = @competencyAssessmentId) AND (ID = @id)",
+ new { competencyAssessmentId, id }
+ );
+ connection.Execute(
+ @"UPDATE SelfAssessmentCollaborators SET IsDeleted=1 WHERE (SelfAssessmentID = @competencyAssessmentId) AND (ID = @id);
+ UPDATE AdminUsers SET IsWorkforceManager = 0 WHERE AdminID = @adminId AND AdminID NOT IN (SELECT DISTINCT AdminID FROM SelfAssessmentCollaborators);",
+ new { competencyAssessmentId, id, adminId }
+ );
+ }
+ public CompetencyAssessmentCollaboratorNotification? GetCollaboratorNotification(int id, int invitedByAdminId)
+ {
+ return connection.Query(
+ @"SELECT
+ sc.SelfAssessmentID,
+ sc.AdminID,
+ sc.CanModify,
+ sc.UserEmail,
+ au.Active AS UserActive,
+ CASE WHEN sc.CanModify = 1 THEN 'Contributor' ELSE 'Reviewer' END AS CompetencyAssessmentRole,
+ sa.[Name] AS CompetencyAssessmentName,
+ (SELECT Forename + ' ' + Surname + (CASE WHEN Active = 1 THEN '' ELSE ' (Inactive)' END) AS Expr1 FROM AdminUsers AS au1 WHERE (AdminID = @invitedByAdminId)) AS InvitedByName,
+ (SELECT Email FROM AdminUsers AS au2 WHERE (AdminID = @invitedByAdminId)) AS InvitedByEmail
+ FROM SelfAssessmentCollaborators AS sc
+ INNER JOIN SelfAssessments AS sa ON sc.SelfAssessmentID = sa.ID
+ INNER JOIN AdminUsers AS au ON sc.AdminID = au.AdminID
+ WHERE (sc.ID = @id) AND (sc.IsDeleted=0)",
+ new { invitedByAdminId, id }
+ ).FirstOrDefault();
+ }
+
+ public bool HasCompetencyWithSignpostedLearning(int competencyAssessmentId)
+ {
+ int hasSignpostedLearning = connection.QueryFirstOrDefault(
+ @"SELECT count(*) FROM SelfAssessmentStructure sas INNER JOIN
+ CompetencyLearningResources clr ON sas.CompetencyID = clr.CompetencyID AND
+ clr.RemovedDate IS NULL
+ WHERE sas.SelfAssessmentID = @competencyAssessmentId",
+ new { competencyAssessmentId });
+
+ return hasSignpostedLearning > 0;
+ }
+ public bool UpdateCompetencyAssessmentOptions(
+ bool includeLearnerDeclarationPrompt,
+ bool includesSignposting,
+ bool linearNavigation,
+ bool useDescriptionExpanders,
+ string? questionLabelText,
+ string? reviewerCommentsLabelText,
+ int competencyAssessmentId, int adminId)
+ {
+ if ((adminId < 1) | (competencyAssessmentId < 1))
+ {
+ logger.LogWarning(
+ $"Not updating role profile name as it failed server side validation. AdminId: {adminId}, competencyAssessmentId: {competencyAssessmentId}"
+ );
+ return false;
+ }
+
+ var numberOfAffectedRows = connection.Execute(
+ @"UPDATE SelfAssessments SET IncludeLearnerDeclarationPrompt = @includeLearnerDeclarationPrompt,
+ IncludesSignposting = @includesSignposting,
+ LinearNavigation = @linearNavigation,
+ UseDescriptionExpanders = @useDescriptionExpanders,
+ QuestionLabel = @questionLabelText,
+ ReviewerCommentsLabel = @reviewerCommentsLabelText,
+ UpdatedByAdminID = @adminId
+ WHERE ID = @competencyAssessmentId ",
+ new
+ {
+ includeLearnerDeclarationPrompt,
+ includesSignposting,
+ linearNavigation,
+ useDescriptionExpanders,
+ questionLabelText,
+ reviewerCommentsLabelText,
+ adminId,
+ competencyAssessmentId
+ }
+ );
+ if (numberOfAffectedRows < 1)
+ {
+ logger.LogWarning(
+ "Not updating options/labels as db update failed. " +
+ $"admin id: {adminId}, competencyAssessmentId: {competencyAssessmentId}"
+ );
+ return false;
+ }
+
+ return true;
+ }
+
+ public bool UpdateCompetencyAssessmentOptionsTaskStatus(int assessmentId, bool taskStatus)
+ {
+ var numberOfAffectedRows = connection.Execute(
+ @"UPDATE SelfAssessmentTaskStatus SET SelfAssessmentOptionsTaskStatus = @taskStatus
+ WHERE SelfAssessmentId = @assessmentId",
+ new { assessmentId, taskStatus }
+ );
+ if (numberOfAffectedRows < 1)
+ {
+ logger.LogWarning(
+ "Not updating SelfAssessmentOptionsTaskStatus as db update failed. " +
+ $"assessmentId: {assessmentId}, taskStatus: {taskStatus}"
+ );
+ return false;
+ }
+ return true;
+ }
+
+ public bool UpdateOptionalCompetenciesInAssessment(int selfAssessmentId, int[] groupIds, int[] selectedStructureIds)
+ {
+ const string sql = @"UPDATE sas
+ SET
+ Optional =
+ CASE
+ WHEN sas.CompetencyGroupID IN @GroupIds THEN 1
+ WHEN sas.ID IN @SelectedIds THEN 1
+ ELSE 0
+ END,
+ GroupOptionalCompetencies =
+ CASE
+ WHEN sas.CompetencyGroupID IN @GroupIds THEN 1
+ ELSE 0
+ END
+ FROM SelfAssessmentStructure sas
+ WHERE sas.SelfAssessmentID = @SelfAssessmentId;";
+
+ var safeGroupIds = (groupIds != null && groupIds.Length > 0)
+ ? groupIds
+ : [-1];
+
+ var safeSelectedIds = (selectedStructureIds != null && selectedStructureIds.Length > 0)
+ ? selectedStructureIds
+ : [-1];
+
+
+ var rows = connection.Execute(sql, new
+ {
+ SelfAssessmentId = selfAssessmentId,
+ GroupIds = safeGroupIds,
+ SelectedIds = safeSelectedIds
+ });
+
+ // Returns true if any rows were updated
+ return rows > 0;
+ }
+
+ public void UpdateMinimumOptionalCompetencies(int selfAssessmentId, int minimumOptionalCompetecies)
+ {
+ connection.Execute(
+ @"UPDATE SelfAssessments
+ SET
+ [MinimumOptionalCompetencies] = @minimumOptionalCompetecies
+ WHERE id = @selfAssessmentId AND ISNULL(MinimumOptionalCompetencies, 0) <> @minimumOptionalCompetecies;"
+ ,
+ new { selfAssessmentId, minimumOptionalCompetecies }
+ );
+ }
+ public void UpdateManageOptionalCompetenciesPrompt(int selfAssessmentId, string? manageOptionalCompetenciesPrompt)
+ {
+ connection.Execute(
+ @"UPDATE SelfAssessments
+ SET
+ [ManageOptionalCompetenciesPrompt] = @manageOptionalCompetenciesPrompt
+ WHERE id = @selfAssessmentId AND ISNULL(ManageOptionalCompetenciesPrompt, '') <> @manageOptionalCompetenciesPrompt;"
+ ,
+ new { selfAssessmentId, manageOptionalCompetenciesPrompt }
+ );
+ }
+
+ public IEnumerable GetCompetencyWithAssessmentQuestionRoleRequirements(int competencyAssessmentId, int? competencyId, int? assessmentQuestionId)
+ {
+ return connection.Query(
+ @"SELECT
+ sas.CompetencyGroupID,
+ cg.Name AS GroupName,
+ sas.CompetencyID,
+ c.Name AS Competency,
+ c.Description AS CompetencyDescription,
+ sas.Optional,
+ caq.AssessmentQuestionID,
+ aq.Question,
+ caq.Required,
+ aqit.InputTypeName,
+ aql.LevelValue AS ResponseValue,
+ aql.LevelLabel AS Response,
+ caqrr.LevelRAG
+FROM SelfAssessmentStructure AS sas
+INNER JOIN Competencies AS c
+ ON sas.CompetencyID = c.ID
+INNER JOIN CompetencyAssessmentQuestions AS caq
+ ON c.ID = caq.CompetencyID
+INNER JOIN AssessmentQuestions AS aq
+ ON caq.AssessmentQuestionID = aq.ID
+INNER JOIN AssessmentQuestionInputTypes AS aqit
+ ON aq.AssessmentQuestionInputTypeID = aqit.ID
+LEFT JOIN CompetencyGroups AS cg
+ ON sas.CompetencyGroupID = cg.ID
+
+INNER JOIN AssessmentQuestionLevels AS aql
+ ON aql.AssessmentQuestionID = aq.ID
+
+LEFT JOIN CompetencyAssessmentQuestionRoleRequirements AS caqrr
+ ON caqrr.AssessmentQuestionID = aq.ID
+ AND caqrr.SelfAssessmentID = sas.SelfAssessmentID
+ AND caqrr.CompetencyID = sas.CompetencyID
+ AND caqrr.LevelValue = aql.LevelValue
+
+WHERE sas.SelfAssessmentID = @competencyAssessmentId
+ AND (@competencyId IS NULL OR sas.CompetencyID = @competencyId)
+ AND (@assessmentQuestionId IS NULL OR caq.AssessmentQuestionID = @assessmentQuestionId)
+
+ORDER BY
+ sas.Ordering,
+ caq.Ordering,
+ aql.LevelValue;",
+ new { competencyAssessmentId, competencyId, assessmentQuestionId }
+ );
+ }
+
+ public bool UpdateCompetencyAssessmentRoleRequirementsTaskStatus(int assessmentId, bool taskStatus)
+ {
+ var numberOfAffectedRows = connection.Execute(
+ @"UPDATE SelfAssessmentTaskStatus SET RoleRequirementsTaskStatus = @taskStatus
+ WHERE SelfAssessmentId = @assessmentId",
+ new { assessmentId, taskStatus }
+ );
+ if (numberOfAffectedRows < 1)
+ {
+ logger.LogWarning(
+ "Not updating RoleRequirementsTaskStatus as db update failed. " +
+ $"assessmentId: {assessmentId}, taskStatus: {taskStatus}"
+ );
+ return false;
+ }
+ return true;
+ }
+
+ public void UpdateRoleRequirementsFlags(int assessmentId, bool enforceRoleRequirementsForSignOff, bool includeRequirementsFilters)
+ {
+ connection.Execute(
+ @"UPDATE SelfAssessments
+ SET
+ [EnforceRoleRequirementsForSignOff] = @enforceRoleRequirementsForSignOff, [IncludeRequirementsFilters] = @includeRequirementsFilters
+ WHERE id = @assessmentId AND (EnforceRoleRequirementsForSignOff <> @enforceRoleRequirementsForSignOff OR IncludeRequirementsFilters <> @includeRequirementsFilters);"
+ ,
+ new { assessmentId, enforceRoleRequirementsForSignOff, includeRequirementsFilters }
+ );
+ }
+
+ public int GetCountOfAsssessmentQuestionInCompetencyAssessment(int competencyAssessmentId, int assessmentQuestionId)
+ {
+ return connection.QuerySingle(
+ @"SELECT COUNT(1)
+ FROM SelfAssessmentStructure AS sas INNER JOIN
+ Competencies AS c ON sas.CompetencyID = c.ID INNER JOIN
+ CompetencyAssessmentQuestions AS caq ON c.ID = caq.CompetencyID
+ WHERE (sas.SelfAssessmentID = @competencyAssessmentId) AND (caq.AssessmentQuestionID = @assessmentQuestionId)",
+ new { competencyAssessmentId, assessmentQuestionId }
+ );
+ }
+
+ public int InsertAssessmentQuestionRoleRequirementForSelfAssessment(int assessmentId, int assessmentQuestionId, int levelValue, int? levelRAG)
+ {
+ var numberOfAffectedRows = connection.Execute(
+ @"INSERT INTO CompetencyAssessmentQuestionRoleRequirements
+ (SelfAssessmentID, CompetencyID, AssessmentQuestionID, LevelValue, LevelRAG)
+ SELECT sas.SelfAssessmentID, sas.CompetencyID, caq.AssessmentQuestionID, @levelValue AS LevelValue, @levelRAG AS LevelRAG
+ FROM SelfAssessmentStructure AS sas INNER JOIN
+ CompetencyAssessmentQuestions AS caq ON sas.CompetencyID = caq.CompetencyID
+ WHERE (sas.SelfAssessmentID = @assessmentId) AND (caq.AssessmentQuestionID = @assessmentQuestionId);"
+ ,
+ new { assessmentId, assessmentQuestionId, levelValue, levelRAG }
+ );
+ return numberOfAffectedRows;
+ }
+
+ public int InsertCompetencyAssessmentQuestionRoleRequirement(int assessmentId, int competencyId, int assessmentQuestionId, int levelValue, int? levelRAG)
+ {
+ var numberOfAffectedRows = connection.Execute(
+ @"INSERT INTO dbo.CompetencyAssessmentQuestionRoleRequirements
+ (SelfAssessmentID
+ ,CompetencyID
+ ,AssessmentQuestionID
+ ,LevelValue
+ ,LevelRAG)
+ VALUES
+ (@assessmentId
+ ,@competencyId
+ ,@assessmentQuestionId
+ ,@levelValue
+ ,@levelRAG);"
+ ,
+ new { assessmentId, competencyId, assessmentQuestionId, levelValue, levelRAG }
+ );
+ return numberOfAffectedRows;
+ }
+
+ public bool DeleteCompetencyAssessmentQuestionRoleRequirement(int assessmentId, int? competencyId, int assessmentQuestionId, int levelValue)
+ {
+ var numberOfAffectedRows = connection.Execute(
+ @"DELETE FROM CompetencyAssessmentQuestionRoleRequirements
+ WHERE SelfAssessmentID = @assessmentId
+ AND AssessmentQuestionID = @assessmentQuestionId
+ AND LevelValue = @levelValue
+ AND (CompetencyID = @competencyId OR @competencyId IS NULL);"
+ ,
+ new { assessmentId, competencyId, assessmentQuestionId, levelValue }
+ );
+ return numberOfAffectedRows > 0;
+ }
+ public void InsertIntoSelfAssessmentCollaboratorsFromFrameworkCollaborators(int selfAssessmentId, int? frameworkId)
+ {
+ var numberOfAffectedRows = connection.Execute(
+ @"INSERT INTO [dbo].[SelfAssessmentCollaborators]
+ (
+ [SelfAssessmentID],
+ [UserEmail],
+ [AdminID],
+ [CanModify],
+ [IsDeleted])
+ SELECT
+ @selfAssessmentID,
+ fc.UserEmail,
+ fc.AdminID,
+ fc.CanModify,
+ fc.IsDeleted
+ FROM FrameworkCollaborators fc
+ INNER JOIN AdminUsers au
+ ON fc.AdminID = au.AdminID
+ WHERE fc.FrameworkID = @frameworkId
+ AND fc.IsDeleted = 0;",
+ new { selfAssessmentId, frameworkId }
+ );
+ }
+ public bool UpdateSupervisorRolesTaskStatus(int competencyAssessmentId, bool taskCompleteChecked)
+ {
+ var numberOfAffectedRows = connection.Execute(
+ @" UPDATE SelfAssessmentTaskStatus
+ SET SupervisorRolesTaskStatus = CASE WHEN @taskCompleteChecked = 1 THEN 1 ELSE 0 END
+ WHERE SelfAssessmentId = @competencyAssessmentId",
+ new { competencyAssessmentId, taskCompleteChecked = taskCompleteChecked ? 1 : 0 }
+ );
+ if (numberOfAffectedRows < 1)
+ {
+ logger.LogWarning(
+ "Not updating SupervisorRolesTaskStatus as db update failed. " +
+ $"competencyAssessmentId: {competencyAssessmentId}, taskCompleteChecked: {taskCompleteChecked}"
+ );
+ return false;
+ }
+ return true;
+ }
+ public bool UpdateSelfAssessments(int competencyAssessmentId,
+ int? signoff,
+ int? confirm,
+ int? supervisorDeclarationValue,
+ string? supervisorCustomText,
+ int? leanerDeclarationValue,
+ string? leanerCustomText
+ )
+ {
+ var sqlQuery = @"
+ IF @signoff = 0
+ BEGIN
+ UPDATE SelfAssessments SET SupervisorSelfAssessmentReview = 0,
+ SupervisorResultsReview = 0,
+ SignOffSupervisorStatement = NULL,
+ SignOffRequestorStatement = NULL
+ WHERE ID = @competencyAssessmentId;
+ END
+ ELSE
+ BEGIN
+ UPDATE SelfAssessments
+ SET SupervisorSelfAssessmentReview = 1,
+ SupervisorResultsReview = @confirm,
+ SignOffSupervisorStatement = CASE WHEN @supervisorDeclarationValue = 0 THEN NULL ELSE @supervisorCustomText END,
+ SignOffRequestorStatement = CASE WHEN @leanerDeclarationValue = 0 THEN NULL ELSE @leanerCustomText END
+ WHERE ID = @competencyAssessmentId;
+ END
+ ";
+ var affectedRows = connection.Execute(
+ sqlQuery,
+ new
+ {
+ competencyAssessmentId,
+ signoff,
+ confirm,
+ supervisorDeclarationValue,
+ supervisorCustomText,
+ leanerDeclarationValue,
+ leanerCustomText
+ }
+ );
+
+ if ((affectedRows < 1))
+ {
+ logger.LogWarning(
+ "Not updating SelfAssessments as db update failed. " +
+ $"competencyAssessmentId: {competencyAssessmentId}, signoff: {signoff}" +
+ $"confirm: {confirm}, supervisorDeclarationValue: {supervisorDeclarationValue} " +
+ $"supervisorCustomText: {supervisorCustomText}, leanerDeclarationValue: {leanerDeclarationValue}, leanerCustomText: {leanerCustomText} "
+ );
+ return false;
+ }
+ return true;
+ }
+ public void UpdateCompetencyAssessmentPublishStatus(int competencyAssessmentId, int status, int adminId)
+ {
+ connection.Execute(
+ @"UPDATE SelfAssessments
+ SET
+ [PublishStatusID] = @status,
+ UpdatedByAdminID = @adminId
+ WHERE id = @competencyAssessmentId;"
+ ,
+ new { competencyAssessmentId, status, adminId }
+ );
+ }
+ public void UpdateCompetencyAssessmentPublish(int competencyAssessmentId, int status, int adminId, bool national, bool pub)
+ {
+ connection.Execute(
+ @"UPDATE SelfAssessments
+ SET
+ [PublishStatusID] = @status,
+ UpdatedByAdminID = @adminId,
+ [National] = @national,
+ [Public] = @pub
+ WHERE id = @competencyAssessmentId;"
+ ,
+ new { competencyAssessmentId, status, adminId, national, pub }
+ );
+ }
+ public void InsertSelfAssessmentReview(int competencyAssessmentId, int selfAssessmentCollaboratorID, bool required)
+ {
+ var exists = (int?)connection.ExecuteScalar(
+ @"SELECT COUNT(*)
+ FROM SelfAssessmentReviews
+ WHERE SelfAssessmentID = @competencyAssessmentId
+ AND SelfAssessmentCollaboratorID = @selfAssessmentCollaboratorID AND Archived IS NULL",
+ new { competencyAssessmentId, selfAssessmentCollaboratorID }
+ );
+ if (exists == 0)
+ {
+ connection.Query(
+ @"INSERT INTO SelfAssessmentReviews
+ (SelfAssessmentID, SelfAssessmentCollaboratorID, SignOffRequired)
+ VALUES
+ (@competencyAssessmentId, @selfAssessmentCollaboratorID, @required)",
+ new { competencyAssessmentId, selfAssessmentCollaboratorID, required }
+ );
+ }
+ }
+ public IEnumerable GetCompetencySelfAssessmentReviews(int competencyAssessmentId)
+ {
+ return connection.Query(
+ @"SELECT SR.ID, SR.SelfAssessmentID, SR.SelfAssessmentCollaboratorID, SC.UserEmail,
+ CAST(CASE WHEN SC.AdminID IS NULL THEN 0 ELSE 1 END AS bit) AS IsRegistered,
+ SR.ReviewRequested, SR.ReviewComplete, SR.SignedOff, SR.SelfAssessmentCommentID, SC1.Comments AS Comment, SR.SignOffRequired
+ FROM SelfAssessmentReviews AS SR INNER JOIN
+ SelfAssessmentCollaborators AS SC ON SR.SelfAssessmentCollaboratorID = SC.ID LEFT OUTER JOIN
+ SelfAssessmentComments AS SC1 ON SR.SelfAssessmentCommentID = SC1.ID
+ WHERE SR.SelfAssessmentID = @competencyAssessmentId AND SR.Archived IS NULL AND (SC.IsDeleted = 0)",
+ new { competencyAssessmentId }
+ );
+ }
+
+ public SelfAssessmentReview? GetCompetencySelfAssessmentReviewById(int competencyAssessmentId, int selfAssessmentReviewId)
+ {
+ return connection.Query(
+ @"SELECT SR.ID, SR.SelfAssessmentID, SR.SelfAssessmentCollaboratorID, SC.UserEmail,
+ CAST(CASE WHEN SC.AdminID IS NULL THEN 0 ELSE 1 END AS bit) AS IsRegistered,
+ SR.ReviewRequested, SR.ReviewComplete, SR.SignedOff, SR.SelfAssessmentCommentID, SC1.Comments AS Comment, SR.SignOffRequired
+ FROM SelfAssessmentReviews AS SR INNER JOIN
+ SelfAssessmentCollaborators AS SC ON SR.SelfAssessmentCollaboratorID = SC.ID LEFT OUTER JOIN
+ SelfAssessmentComments AS SC1 ON SR.SelfAssessmentCommentID = SC1.ID
+ WHERE SR.SelfAssessmentID = @competencyAssessmentId AND SR.ID = @selfAssessmentReviewId AND SR.Archived IS NULL AND (SC.IsDeleted = 0)",
+ new { competencyAssessmentId, selfAssessmentReviewId }
+ ).FirstOrDefault();
+ }
+ public void ArchiveSelfAssessmentReviewRequest(int reviewId)
+ {
+ var numberOfAffectedRows = connection.Execute(
+ @"UPDATE SelfAssessmentReviews
+ SET Archived = GETUTCDATE()
+ WHERE ID = @reviewId",
+ new { reviewId }
+ );
+ if (numberOfAffectedRows < 1)
+ {
+ logger.LogWarning(
+ "Not archiving framework review as db update failed. " +
+ $"reviewId: {reviewId}."
+ );
+ }
+ }
+ public void UpdateSelfAssessmentReview(int selfAssessmentID, int reviewId, bool signedOff, int? commentId)
+ {
+ var numberOfAffectedRows = connection.Execute(
+ @"UPDATE SelfAssessmentReviews
+ SET ReviewComplete = GETUTCDATE(), SelfAssessmentCommentID = @commentId, SignedOff = @signedOff
+ WHERE ID = @reviewId AND SelfAssessmentID = @selfAssessmentID",
+ new { reviewId, commentId, signedOff, selfAssessmentID }
+ );
+ if (numberOfAffectedRows < 1)
+ {
+ logger.LogWarning(
+ "Not submitting selfAssessment review as db update failed. " +
+ $"commentId: {commentId}, frameworkId: {selfAssessmentID}, reviewId: {reviewId}, signedOff: {signedOff} ."
+ );
+ }
+ }
+ public int InsertComment(int selfAssessmentID, int adminId, string comment, int? replyToCommentId)
+ {
+ if ((selfAssessmentID < 1) | (adminId < 1) | (comment == null))
+ {
+ logger.LogWarning(
+ $"Not inserting selfAssessment comment as it failed server side validation. AdminId: {adminId}, selfAssessmentID: {selfAssessmentID}, comment: {comment}"
+ );
+ }
+
+ var commentId = connection.ExecuteScalar(
+ @"INSERT INTO SelfAssessmentComments
+ (AdminID
+ ,ReplyToSelfAssessmentCommentID
+ ,Comments
+ ,SelfAssessmentID)
+ VALUES (@adminId, @replyToCommentId, @comment, @selfAssessmentID);
+ SELECT CAST(SCOPE_IDENTITY() as int)",
+ new { adminId, replyToCommentId, comment, selfAssessmentID }
+ );
+ if (commentId < 1)
+ {
+ logger.LogWarning(
+ "Not inserting selfAssessment comment as db insert failed. " +
+ $"AdminId: {adminId}, selfAssessmentID: {selfAssessmentID}, comment: {comment}."
+ );
+ }
+
+ return commentId;
+ }
+ public SelfAssessmentReviewOutcomeNotification? GetSelfAssessmentReviewNotification(int reviewId)
+ {
+ return connection.Query(
+ @"SELECT
+ sr.ID,
+ sr.SelfAssessmentID,
+ sr.SelfAssessmentCollaboratorID,
+ sc.UserEmail,
+ CAST(CASE WHEN sc.AdminID IS NULL THEN 0 ELSE 1 END AS bit) AS IsRegistered,
+ sr.ReviewRequested,
+ sr.ReviewComplete,
+ sr.SignedOff,
+ sr.SelfAssessmentCommentID,
+ sc1.Comments AS Comment,
+ sr.SignOffRequired,
+ AU.Forename AS ReviewerFirstName,
+ AU.Surname AS ReviewerLastName,
+ AU.Active AS ReviewerActive,
+ AU1.Forename AS OwnerFirstName,
+ AU1.Email AS OwnerEmail,
+ s.Name AS SelfAssessmentName
+ FROM SelfAssessmentReviews AS sr
+ INNER JOIN SelfAssessmentCollaborators AS sc ON sr.SelfAssessmentCollaboratorID = sc.ID AND sc.IsDeleted = 0
+ INNER JOIN AdminUsers AS AU ON sc.AdminID = AU.AdminID
+ INNER JOIN SelfAssessments AS s ON sr.SelfAssessmentID = s.ID
+ INNER JOIN AdminUsers AS AU1 ON s.CreatedByAdminID = AU1.AdminID
+ LEFT OUTER JOIN SelfAssessmentComments AS sc1 ON sr.SelfAssessmentCommentID = sc1.ID
+ WHERE (sr.ID = @reviewId) AND (sr.ReviewComplete IS NOT NULL)",
+ new { reviewId }
+ ).FirstOrDefault();
+ }
+ public void UpdateReviewRequestedDate(int reviewId)
+ {
+ var numberOfAffectedRows = connection.Execute(
+ @"UPDATE SelfAssessmentReviews
+ SET ReviewRequested = GETUTCDATE()
+ WHERE ID = @reviewId",
+ new { reviewId }
+ );
+ if (numberOfAffectedRows < 1)
+ {
+ logger.LogWarning(
+ "Not updating selfAssessment review requested date as db update failed. " +
+ $"reviewId: {reviewId}."
+ );
+ }
+ }
+ public int InsertCompetencySelfAssessmentReview(int reviewId)
+ {
+ ArchiveSelfAssessmentReviewRequest(reviewId);
+ var id = connection.QuerySingle(
+ @"INSERT INTO SelfAssessmentReviews
+ (SelfAssessmentID, SelfAssessmentCollaboratorID, SignOffRequired)
+ OUTPUT INSERTED.ID
+ SELECT sr1.SelfAssessmentID, sr1.SelfAssessmentCollaboratorID, sr1.SignOffRequired FROM SelfAssessmentReviews AS sr1 WHERE sr1.ID = @reviewId",
+ new { reviewId }
+ );
+ if (id < 1)
+ {
+ logger.LogWarning(
+ "Not inserting assessment question as db update failed. " +
+ $"reviewId: {reviewId}"
+ );
+ return 0;
+ }
+
+ return id;
+ }
+ public bool UpdateCompetencyAssessmentReviewTaskStatus(int assessmentId, bool taskStatus)
+ {
+ var numberOfAffectedRows = connection.Execute(
+ @"UPDATE SelfAssessmentTaskStatus SET ReviewTaskStatus = @taskStatus
+ WHERE SelfAssessmentId = @assessmentId",
+ new { assessmentId, taskStatus }
+ );
+ if (numberOfAffectedRows < 1)
+ {
+ logger.LogWarning(
+ "Not updating ReviewTaskStatus as db update failed. " +
+ $"assessmentId: {assessmentId}, taskStatus: {taskStatus}"
+ );
+ return false;
+ }
+ return true;
+ }
+ }
+}
diff --git a/DigitalLearningSolutions.Data/DataServices/CompetencyLearningResourcesDataService.cs b/DigitalLearningSolutions.Data/DataServices/CompetencyLearningResourcesDataService.cs
index 323188bf45..f00b2b1a71 100644
--- a/DigitalLearningSolutions.Data/DataServices/CompetencyLearningResourcesDataService.cs
+++ b/DigitalLearningSolutions.Data/DataServices/CompetencyLearningResourcesDataService.cs
@@ -14,6 +14,7 @@ public interface ICompetencyLearningResourcesDataService
IEnumerable GetCompetencyResourceAssessmentQuestionParameters(IEnumerable competencyLearningResourceIds);
int AddCompetencyLearningResource(int resourceRefID, string originalResourceName, string description, string resourceType, string link, string catalogue, decimal rating, int competencyID, int adminId);
+ IEnumerable GetActiveCompetencyLearningResourcesByCompetencyIdAndReferenceId(int competencyId, int referenceId);
}
public class CompetencyLearningResourcesDataService : ICompetencyLearningResourcesDataService
@@ -120,5 +121,20 @@ FROM CompetencyResourceAssessmentQuestionParameters
new { competencyLearningResourceIds }
);
}
+ public IEnumerable GetActiveCompetencyLearningResourcesByCompetencyIdAndReferenceId(int competencyId, int referenceId)
+ {
+ return connection.Query(
+ @"SELECT
+ clr.ID,
+ clr.CompetencyID,
+ clr.LearningResourceReferenceID,
+ clr.AdminID,
+ lrr.ResourceRefID AS LearningHubResourceReferenceId
+ FROM CompetencyLearningResources AS clr
+ INNER JOIN LearningResourceReferences AS lrr ON lrr.ID = clr.LearningResourceReferenceID
+ WHERE CompetencyID = @competencyId AND ResourceRefID = @referenceId AND clr.RemovedDate IS NULL",
+ new { competencyId, referenceId }
+ );
+ }
}
}
diff --git a/DigitalLearningSolutions.Data/DataServices/CourseDataService.cs b/DigitalLearningSolutions.Data/DataServices/CourseDataService.cs
index 06196293a0..f7c387cf96 100644
--- a/DigitalLearningSolutions.Data/DataServices/CourseDataService.cs
+++ b/DigitalLearningSolutions.Data/DataServices/CourseDataService.cs
@@ -122,7 +122,7 @@ IEnumerable GetCourseDelegatesForExport(string searchSt
int customisationId, int centreId, bool? isDelegateActive, bool? isProgressLocked, bool? removed, bool? hasCompleted, string? answer1, string? answer2, string? answer3);
int EnrolOnActivitySelfAssessment(int selfAssessmentId, int candidateId, int supervisorId, string adminEmail,
- int selfAssessmentSupervisorRoleId, DateTime? completeByDate, int delegateUserId, int centreId, int? enrolledByAdminId);
+ int selfAssessmentSupervisorRoleId, DateTime? completeByDate, int delegateUserId, int centreId, int? enrolledByAdminId, bool isNonReportable = false);
bool IsCourseCompleted(int candidateId, int customisationId);
bool IsCourseCompleted(int candidateId, int customisationId, int progressID);
@@ -443,7 +443,7 @@ public void RemoveCurrentCourse(int progressId, int candidateId, RemovalMethod r
}
public int EnrolOnActivitySelfAssessment(int selfAssessmentId, int candidateId, int supervisorId, string adminEmail,
- int selfAssessmentSupervisorRoleId, DateTime? completeByDate, int delegateUserId, int centreId, int? enrolledByAdminId)
+ int selfAssessmentSupervisorRoleId, DateTime? completeByDate, int delegateUserId, int centreId, int? enrolledByAdminId, bool isNonReportable = false)
{
IClockUtility clockUtility = new ClockUtility();
DateTime startedDate = clockUtility.UtcNow;
@@ -473,7 +473,8 @@ FROM CandidateAssessments
,[CompleteByDate]
,[CentreID]
,[EnrolmentMethodId]
- ,[EnrolledByAdminId])
+ ,[EnrolledByAdminId]
+ ,[NonReportable])
OUTPUT INSERTED.Id
VALUES
(@DelegateUserID,
@@ -483,8 +484,9 @@ OUTPUT INSERTED.Id
@completeByDateDynamic,
@centreId,
@enrolmentMethodId,
- @enrolledByAdminId);",
- new { delegateUserId, selfAssessmentId, startedDate, lastAccessed, completeByDateDynamic, centreId, enrolmentMethodId, enrolledByAdminId }
+ @enrolledByAdminId,
+ @isNonReportable);",
+ new { delegateUserId, selfAssessmentId, startedDate, lastAccessed, completeByDateDynamic, centreId, enrolmentMethodId, enrolledByAdminId, isNonReportable }
);
}
@@ -514,7 +516,8 @@ INNER JOIN Users AS U
LEFT OUTER JOIN UserCentreDetails AS UCD ON
DA.UserID = UCD.UserID AND
DA.CentreID = UCD.CentreID
- WHERE (DA.UserID = @delegateUserId)", new { supervisorId, delegateUserId, adminEmail });
+ WHERE (DA.UserID = @delegateUserId AND DA.CentreID = @centreId)",
+ new { supervisorId, delegateUserId, adminEmail, centreId });
}
if (candidateAssessmentId > 0 && supervisorDelegateId > 0 && selfAssessmentSupervisorRoleId > 0)
diff --git a/DigitalLearningSolutions.Data/DataServices/FrameworkDataService.cs b/DigitalLearningSolutions.Data/DataServices/FrameworkDataService.cs
index 58b1f01ab9..2c7d07013a 100644
--- a/DigitalLearningSolutions.Data/DataServices/FrameworkDataService.cs
+++ b/DigitalLearningSolutions.Data/DataServices/FrameworkDataService.cs
@@ -52,9 +52,9 @@ public interface IFrameworkDataService
CollaboratorNotification? GetCollaboratorNotification(int id, int invitedByAdminId);
// Competencies/groups:
- IEnumerable GetFrameworkCompetencyGroups(int frameworkId);
+ IEnumerable GetFrameworkCompetencyGroups(int frameworkId, int? assessmentId);
- IEnumerable GetFrameworkCompetenciesUngrouped(int frameworkId);
+ IEnumerable GetFrameworkCompetenciesUngrouped(int frameworkId, int? assessmentId);
CompetencyGroupBase? GetCompetencyGroupBaseById(int Id);
@@ -315,7 +315,7 @@ FROM FrameworkCollaborators fc
AND aa3.Active = 1 ORDER BY fwr.ID DESC) AS FrameworkReviewID";
private const string BrandedFrameworkFields =
- @",(SELECT BrandName
+ @", FW.Description, FW.FrameworkConfig AS Vocabulary, (SELECT BrandName
FROM Brands
WHERE (BrandID = FW.BrandID)) AS Brand,
(SELECT CategoryName
@@ -866,18 +866,23 @@ public void RemoveCustomFlag(int flagId)
);
}
- public IEnumerable GetFrameworkCompetencyGroups(int frameworkId)
+ public IEnumerable GetFrameworkCompetencyGroups(int frameworkId, int? assessmentId)
{
+ var assessmentFilter = assessmentId.HasValue ?
+ @$"AND c.ID NOT IN (SELECT CompetencyID
+ FROM SelfAssessmentStructure
+ WHERE (SelfAssessmentID = {assessmentId}))"
+ : string.Empty;
var result = connection.Query(
- @"SELECT fcg.ID, fcg.CompetencyGroupID, cg.Name, cg.Description, fcg.Ordering, fc.ID, c.ID AS CompetencyID, c.Name, c.Description, fc.Ordering, COUNT(caq.AssessmentQuestionID) AS AssessmentQuestions
+ @$"SELECT fcg.ID, fcg.CompetencyGroupID, cg.Name, fcg.Ordering, fc.ID, c.ID AS CompetencyID, c.Name, c.Description, fc.Ordering,
+ (SELECT COUNT(*) FROM CompetencyAssessmentQuestions caq WHERE caq.CompetencyID = c.ID) AS AssessmentQuestions
,(SELECT COUNT(*) FROM CompetencyLearningResources clr WHERE clr.CompetencyID = c.ID AND clr.RemovedDate IS NULL) AS CompetencyLearningResourcesCount
FROM FrameworkCompetencyGroups AS fcg INNER JOIN
CompetencyGroups AS cg ON fcg.CompetencyGroupID = cg.ID LEFT OUTER JOIN
FrameworkCompetencies AS fc ON fcg.ID = fc.FrameworkCompetencyGroupID LEFT OUTER JOIN
- Competencies AS c ON fc.CompetencyID = c.ID LEFT OUTER JOIN
- CompetencyAssessmentQuestions AS caq ON c.ID = caq.CompetencyID
- WHERE (fcg.FrameworkID = @frameworkId)
- GROUP BY fcg.ID, fcg.CompetencyGroupID, cg.Name, cg.Description, fcg.Ordering, fc.ID, c.ID, c.Name, c.Description, fc.Ordering
+ Competencies AS c ON fc.CompetencyID = c.ID
+ WHERE (fcg.FrameworkID = @frameworkId) {assessmentFilter}
+ GROUP BY fcg.ID, fcg.CompetencyGroupID, cg.Name, fcg.Ordering, fc.ID, c.ID, c.Name, c.Description, fc.Ordering
ORDER BY fcg.Ordering, fc.Ordering",
(frameworkCompetencyGroup, frameworkCompetency) =>
{
@@ -887,29 +892,36 @@ FROM FrameworkCompetencyGroups AS fcg INNER JOIN
},
new { frameworkId }
);
- return result.GroupBy(frameworkCompetencyGroup => frameworkCompetencyGroup.CompetencyGroupID).Select(
- group =>
- {
- var groupedFrameworkCompetencyGroup = group.First();
- groupedFrameworkCompetencyGroup.FrameworkCompetencies = group.Where(frameworkCompetencyGroup => frameworkCompetencyGroup.FrameworkCompetencies.Count > 0)
- .Select(
- frameworkCompetencyGroup => frameworkCompetencyGroup.FrameworkCompetencies.Single()
- ).ToList();
- return groupedFrameworkCompetencyGroup;
- }
- );
+ return result
+ .GroupBy(fcg => fcg.CompetencyGroupID)
+ .Select(group =>
+ {
+ var groupedFrameworkCompetencyGroup = group.First();
+
+ // Flatten all FrameworkCompetencies from all instances in this group
+ groupedFrameworkCompetencyGroup.FrameworkCompetencies = group
+ .SelectMany(g => g.FrameworkCompetencies)
+ .Where(fc => fc != null).
+ Distinct().ToList();
+
+ return groupedFrameworkCompetencyGroup;
+ });
}
- public IEnumerable GetFrameworkCompetenciesUngrouped(int frameworkId)
+ public IEnumerable GetFrameworkCompetenciesUngrouped(int frameworkId, int? assessmentId)
{
+ var assessmentFilter = assessmentId.HasValue ?
+ @$"AND c.ID NOT IN (SELECT CompetencyID
+ FROM SelfAssessmentStructure
+ WHERE (SelfAssessmentID = {assessmentId}))"
+ : string.Empty;
return connection.Query(
- @"SELECT fc.ID, c.ID AS CompetencyID, c.Name, c.Description, fc.Ordering, COUNT(caq.AssessmentQuestionID) AS AssessmentQuestions,(select COUNT(CompetencyId) from CompetencyLearningResources where CompetencyID=c.ID) AS CompetencyLearningResourcesCount
+ @$"SELECT fc.ID, c.ID AS CompetencyID, c.Name, c.Description, fc.Ordering,
+ (SELECT COUNT(*) FROM CompetencyAssessmentQuestions caq WHERE caq.CompetencyID = c.ID) AS AssessmentQuestions,(select COUNT(CompetencyId) from CompetencyLearningResources where CompetencyID=c.ID) AS CompetencyLearningResourcesCount
FROM FrameworkCompetencies AS fc
INNER JOIN Competencies AS c ON fc.CompetencyID = c.ID
- LEFT OUTER JOIN
- CompetencyAssessmentQuestions AS caq ON c.ID = caq.CompetencyID
WHERE fc.FrameworkID = @frameworkId
- AND fc.FrameworkCompetencyGroupID IS NULL
+ AND fc.FrameworkCompetencyGroupID IS NULL {assessmentFilter}
GROUP BY fc.ID, c.ID, c.Name, c.Description, fc.Ordering
ORDER BY fc.Ordering",
new { frameworkId }
@@ -2072,7 +2084,7 @@ FROM FrameworkComments
public IEnumerable GetReviewersForFrameworkId(int frameworkId)
{
return connection.Query(
- @"SELECT
+ @"SELECT DISTINCT
fc.ID,
fc.FrameworkID,
fc.AdminID,
@@ -2083,8 +2095,11 @@ public IEnumerable GetReviewersForFrameworkId(int frameworkI
FROM FrameworkCollaborators fc
INNER JOIN AdminUsers AS au ON fc.AdminID = au.AdminID
LEFT OUTER JOIN FrameworkReviews ON fc.ID = FrameworkReviews.FrameworkCollaboratorID
- WHERE (fc.FrameworkID = @FrameworkID) AND (FrameworkReviews.ID IS NULL) AND (fc.IsDeleted=0) OR
- (fc.FrameworkID = @FrameworkID) AND (FrameworkReviews.Archived IS NOT NULL) AND (fc.IsDeleted=0)",
+ WHERE ((fc.FrameworkID = @FrameworkID) AND (FrameworkReviews.ID IS NULL) AND (fc.IsDeleted=0)) OR
+ ((fc.FrameworkID = @FrameworkID) AND (FrameworkReviews.Archived IS NOT NULL) AND (fc.IsDeleted=0))
+ AND
+ (fc.ID Not in ( Select FrameworkCollaboratorID from FrameworkReviews where FrameworkID = @FrameworkID AND
+ FrameworkReviews.Archived IS NULL AND FrameworkCollaboratorID = fc.ID))",
new { frameworkId }
);
}
@@ -2266,7 +2281,7 @@ public void ArchiveReviewRequest(int reviewId)
FROM FrameworkCollaborators
WHERE (FrameworkID = FW.ID) AND (IsDeleted=0)))) AS MyFrameworksCount,
- (SELECT COUNT(*) FROM SelfAssessments) AS RoleProfileCount,
+ (SELECT COUNT(*) FROM SelfAssessments) AS CompetencyAssessmentCount,
(SELECT COUNT(*) FROM SelfAssessments AS RP LEFT OUTER JOIN
SelfAssessmentCollaborators AS RPC ON RPC.SelfAssessmentID = RP.ID AND RPC.AdminID = @adminId
@@ -2274,7 +2289,7 @@ FROM FrameworkCollaborators
(@adminId IN
(SELECT AdminID
FROM SelfAssessmentCollaborators
- WHERE (SelfAssessmentID = RP.ID)))) AS MyRoleProfileCount",
+ WHERE (SelfAssessmentID = RP.ID)))) AS MyCompetencyAssessmentCount",
new { adminId }
).FirstOrDefault();
}
@@ -2284,7 +2299,7 @@ public IEnumerable GetDashboardToDoItems(int adminId)
return connection.Query(
@"SELECT
FW.ID AS FrameworkID,
- 0 AS RoleProfileID,
+ 0 AS SelfAssessmentID,
FW.FrameworkName AS ItemName,
AU.Forename + ' ' + AU.Surname + (CASE WHEN AU.Active = 1 THEN '' ELSE ' (Inactive)' END) AS RequestorName,
FWR.SignOffRequired,
@@ -2296,8 +2311,8 @@ FROM FrameworkReviews AS FWR
WHERE (FWC.AdminID = @adminId) AND (FWR.ReviewComplete IS NULL) AND (FWR.Archived IS NULL)
UNION ALL
SELECT
- 0 AS SelfAssessmentID,
- RP.ID AS SelfAssessmentID,
+ 0 AS FrameworkID,
+ RP.ID AS CompetencyAssessmentID,
RP.Name AS ItemName,
AU.Forename + ' ' + AU.Surname + (CASE WHEN AU.Active = 1 THEN '' ELSE ' (Inactive)' END) AS RequestorName,
RPR.SignOffRequired,
diff --git a/DigitalLearningSolutions.Data/DataServices/RoleProfileDataService.cs b/DigitalLearningSolutions.Data/DataServices/RoleProfileDataService.cs
deleted file mode 100644
index 7e37d4ff03..0000000000
--- a/DigitalLearningSolutions.Data/DataServices/RoleProfileDataService.cs
+++ /dev/null
@@ -1,197 +0,0 @@
-namespace DigitalLearningSolutions.Data.DataServices
-{
- using System.Collections.Generic;
- using System.Data;
- using System.Linq;
- using Dapper;
- using DigitalLearningSolutions.Data.Models.RoleProfiles;
- using Microsoft.Extensions.Logging;
-
- public interface IRoleProfileDataService
- {
- //GET DATA
- IEnumerable GetAllRoleProfiles(int adminId);
-
- IEnumerable GetRoleProfilesForAdminId(int adminId);
-
- RoleProfileBase? GetRoleProfileBaseById(int roleProfileId, int adminId);
-
- RoleProfileBase? GetRoleProfileByName(string roleProfileName, int adminId);
-
- IEnumerable GetNRPProfessionalGroups();
-
- //UPDATE DATA
- bool UpdateRoleProfileName(int roleProfileId, int adminId, string roleProfileName);
-
- bool UpdateRoleProfileProfessionalGroup(int roleProfileId, int adminId, int? nrpProfessionalGroupID);
- //INSERT DATA
-
- //DELETE DATA
- }
-
- public class RoleProfileDataService : IRoleProfileDataService
- {
- private const string SelfAssessmentBaseFields = @"rp.ID, rp.Name AS RoleProfileName, rp.Description, rp.BrandID,
- rp.ParentSelfAssessmentID,
- rp.[National], rp.[Public], rp.CreatedByAdminID AS OwnerAdminID,
- rp.NRPProfessionalGroupID,
- rp.NRPSubGroupID,
- rp.NRPRoleID,
- rp.PublishStatusID, CASE WHEN rp.CreatedByAdminID = @adminId THEN 3 WHEN rpc.CanModify = 1 THEN 2 WHEN rpc.CanModify = 0 THEN 1 ELSE 0 END AS UserRole";
-
- private const string SelfAssessmentFields =
- @", rp.CreatedDate,
- (SELECT BrandName
- FROM Brands
- WHERE (BrandID = rp.BrandID)) AS Brand,
- (SELECT [Name]
- FROM SelfAssessments AS rp2
- WHERE (ID = rp.ParentSelfAssessmentID)) AS ParentSelfAssessment,
- (SELECT Forename + ' ' + Surname + (CASE WHEN Active = 1 THEN '' ELSE ' (Inactive)' END) AS Expr1
- FROM AdminUsers
- WHERE (AdminID = rp.CreatedByAdminID)) AS Owner,
- rp.Archived,
- rp.LastEdit,
- (SELECT ProfessionalGroup
- FROM NRPProfessionalGroups
- WHERE (ID = rp.NRPProfessionalGroupID)) AS NRPProfessionalGroup,
- (SELECT SubGroup
- FROM NRPSubGroups
- WHERE (ID = rp.NRPSubGroupID)) AS NRPSubGroup,
- (SELECT RoleProfile
- FROM NRPRoles
- WHERE (ID = rp.NRPRoleID)) AS NRPRole, rpr.ID AS SelfAssessmentReviewID";
-
- private const string SelfAssessmentBaseTables =
- @"SelfAssessments AS rp LEFT OUTER JOIN
- SelfAssessmentCollaborators AS rpc ON rpc.SelfAssessmentID = rp.ID AND rpc.AdminID = @adminId";
-
- private const string SelfAssessmentTables =
- @" LEFT OUTER JOIN
- SelfAssessmentReviews AS rpr ON rpc.ID = rpr.SelfAssessmentCollaboratorID AND rpr.Archived IS NULL AND rpr.ReviewComplete IS NULL";
-
- private readonly IDbConnection connection;
- private readonly ILogger logger;
-
- public RoleProfileDataService(IDbConnection connection, ILogger logger)
- {
- this.connection = connection;
- this.logger = logger;
- }
-
- public IEnumerable GetAllRoleProfiles(int adminId)
- {
- return connection.Query(
- $@"SELECT {SelfAssessmentBaseFields} {SelfAssessmentFields}
- FROM {SelfAssessmentBaseTables} {SelfAssessmentTables}",
- new { adminId }
- );
- }
-
- public IEnumerable GetRoleProfilesForAdminId(int adminId)
- {
- return connection.Query(
- $@"SELECT {SelfAssessmentBaseFields} {SelfAssessmentFields}
- FROM {SelfAssessmentBaseTables} {SelfAssessmentTables}
- WHERE (rp.CreatedByAdminID = @adminId) OR
- (@adminId IN
- (SELECT AdminID
- FROM SelfAssessmentCollaborators
- WHERE (SelfAssessmentID = rp.ID)))",
- new { adminId }
- );
- }
-
- public RoleProfileBase? GetRoleProfileBaseById(int roleProfileId, int adminId)
- {
- return connection.Query(
- $@"SELECT {SelfAssessmentBaseFields}
- FROM {SelfAssessmentBaseTables}
- WHERE (rp.ID = @roleProfileId)",
- new { roleProfileId, adminId }
- ).FirstOrDefault();
- }
-
- public bool UpdateRoleProfileName(int roleProfileId, int adminId, string roleProfileName)
- {
- if ((roleProfileName.Length == 0) | (adminId < 1) | (roleProfileId < 1))
- {
- logger.LogWarning(
- $"Not updating role profile name as it failed server side validation. AdminId: {adminId}, roleProfileName: {roleProfileName}, roleProfileId: {roleProfileId}"
- );
- return false;
- }
-
- var existingSelfAssessments = (int)connection.ExecuteScalar(
- @"SELECT COUNT(*) FROM SelfAssessments WHERE [Name] = @roleProfileName AND ID <> @roleProfileId",
- new { roleProfileName, roleProfileId }
- );
- if (existingSelfAssessments > 0)
- {
- return false;
- }
-
- var numberOfAffectedRows = connection.Execute(
- @"UPDATE SelfAssessments SET [Name] = @roleProfileName, UpdatedByAdminID = @adminId
- WHERE ID = @roleProfileId",
- new { roleProfileName, adminId, roleProfileId }
- );
- if (numberOfAffectedRows < 1)
- {
- logger.LogWarning(
- "Not updating role profile name as db update failed. " +
- $"SelfAssessmentName: {roleProfileName}, admin id: {adminId}, roleProfileId: {roleProfileId}"
- );
- return false;
- }
-
- return true;
- }
-
- public IEnumerable GetNRPProfessionalGroups()
- {
- return connection.Query(
- @"SELECT ID, ProfessionalGroup, Active
- FROM NRPProfessionalGroups
- WHERE (Active = 1)
- ORDER BY ProfessionalGroup"
- );
- }
-
- public RoleProfileBase? GetRoleProfileByName(string roleProfileName, int adminId)
- {
- return connection.Query(
- $@"SELECT {SelfAssessmentBaseFields}
- FROM {SelfAssessmentBaseTables}
- WHERE (rp.Name = @roleProfileName)",
- new { roleProfileName, adminId }
- ).FirstOrDefault();
- }
-
- public bool UpdateRoleProfileProfessionalGroup(int roleProfileId, int adminId, int? nrpProfessionalGroupID)
- {
- var sameCount = (int)connection.ExecuteScalar(
- @"SELECT COUNT(*) FROM RoleProfiles WHERE ID = @roleProfileId AND NRPProfessionalGroupID = @nrpProfessionalGroupID",
- new { roleProfileId, nrpProfessionalGroupID }
- );
- if (sameCount > 0)
- {
- //same so don't update:
- return false;
- }
-
- //needs updating:
- var numberOfAffectedRows = connection.Execute(
- @"UPDATE SelfAssessments SET NRPProfessionalGroupID = @nrpProfessionalGroupID, NRPSubGroupID = NULL, NRPRoleID = NULL, UpdatedByAdminID = @adminId
- WHERE ID = @roleProfileId",
- new { nrpProfessionalGroupID, adminId, roleProfileId }
- );
- if (numberOfAffectedRows > 0)
- {
- return true;
- }
-
- return false;
- }
- }
-}
diff --git a/DigitalLearningSolutions.Data/DataServices/SelfAssessmentDataService/CandidateAssessmentsDataService.cs b/DigitalLearningSolutions.Data/DataServices/SelfAssessmentDataService/CandidateAssessmentsDataService.cs
index e6cbd3cdf5..90f8133a56 100644
--- a/DigitalLearningSolutions.Data/DataServices/SelfAssessmentDataService/CandidateAssessmentsDataService.cs
+++ b/DigitalLearningSolutions.Data/DataServices/SelfAssessmentDataService/CandidateAssessmentsDataService.cs
@@ -186,6 +186,7 @@ CandidateAssessments AS CA LEFT OUTER JOIN
SA.SupervisorSelfAssessmentReview,
SA.ReviewerCommentsLabel,
SA.EnforceRoleRequirementsForSignOff,
+ SA.RetirementDate,
COALESCE(SA.Vocabulary, 'Capability') AS Vocabulary,
COUNT(C.ID) AS NumberOfCompetencies,
CA.StartedDate,
@@ -223,7 +224,8 @@ FROM SelfAssessmentSupervisorRoles AS SelfAssessmentSupervisorRoles_1
SA.ManageSupervisorsDescription,
CA.NonReportable,
U.FirstName +' '+ U.LastName AS DelegateName,
- SA.MinimumOptionalCompetencies
+ SA.MinimumOptionalCompetencies,
+ SA.IncludeLearnerDeclarationPrompt
FROM CandidateAssessments CA
JOIN SelfAssessments SA
ON CA.SelfAssessmentID = SA.ID
@@ -248,7 +250,8 @@ GROUP BY
CA.LaunchCount, CA.SubmittedDate, SA.LinearNavigation, SA.UseDescriptionExpanders,
SA.ManageOptionalCompetenciesPrompt, SA.SupervisorSelfAssessmentReview, SA.SupervisorResultsReview,
SA.ReviewerCommentsLabel,SA.EnforceRoleRequirementsForSignOff, SA.ManageSupervisorsDescription,CA.NonReportable,
- U.FirstName , U.LastName,SA.MinimumOptionalCompetencies, CA.SelfAssessmentProcessAgreed",
+ U.FirstName , U.LastName,SA.MinimumOptionalCompetencies, CA.SelfAssessmentProcessAgreed, SA.IncludeLearnerDeclarationPrompt,
+ SA.RetirementDate",
new { delegateUserId, selfAssessmentId }
);
}
diff --git a/DigitalLearningSolutions.Data/DataServices/SelfAssessmentDataService/SelfAssessmentDataService.cs b/DigitalLearningSolutions.Data/DataServices/SelfAssessmentDataService/SelfAssessmentDataService.cs
index bc47f8eb21..c01d1ec1b1 100644
--- a/DigitalLearningSolutions.Data/DataServices/SelfAssessmentDataService/SelfAssessmentDataService.cs
+++ b/DigitalLearningSolutions.Data/DataServices/SelfAssessmentDataService/SelfAssessmentDataService.cs
@@ -296,7 +296,12 @@ FROM SelfAssessments
COALESCE(ucd.Email, u.PrimaryEmail) AS DelegateEmail,
da.Active AS IsDelegateActive,
sa.Name AS [Name],
- MAX(casv.Verified) as SignedOff,
+ CASE
+ WHEN MAX(sar.[DateTime]) >= MAX(casv.Verified) THEN NULL
+ ELSE (
+ MAX(casv.Verified)
+ )
+ END AS SignedOff,
sa.SupervisorSelfAssessmentReview,
sa.SupervisorResultsReview";
@@ -311,6 +316,8 @@ LEFT OUTER JOIN Users AS uEnrolledBy WITH (NOLOCK) ON uEnrolledBy.ID = aaEnrolle
LEFT JOIN dbo.CandidateAssessmentSupervisors AS cas WITH (NOLOCK) ON ca.ID = cas.CandidateAssessmentID
LEFT JOIN dbo.CandidateAssessmentSupervisorVerifications AS casv WITH (NOLOCK) ON cas.ID = casv.CandidateAssessmentSupervisorID AND
(casv.Verified IS NOT NULL AND casv.SignedOff = 1)
+ LEFT JOIN SelfAssessmentResults AS sar ON ca.SelfAssessmentID = sar.SelfAssessmentID AND
+ ca.DelegateUserID = sar.DelegateUserID
WHERE sa.ID = @selfAssessmentId
AND da.CentreID = @centreID AND csa.CentreID = @centreID
@@ -349,7 +356,8 @@ LEFT JOIN dbo.CandidateAssessmentSupervisorVerifications AS casv WITH (NOLOCK) O
if (signedOff != null)
{
- groupBy += (bool)signedOff ? " HAVING MAX(casv.Verified) IS NOT NULL " : " HAVING MAX(casv.Verified) IS NULL ";
+ groupBy += (bool)signedOff ? " HAVING MAX(sar.[DateTime]) < MAX(casv.Verified) AND MAX(casv.Verified) IS NOT NULL " :
+ " HAVING MAX(sar.[DateTime]) >= MAX(casv.Verified) OR MAX(casv.Verified) IS NULL ";
}
string orderBy;
diff --git a/DigitalLearningSolutions.Data/DataServices/SelfAssessmentDataService/SelfAssessmentSupervisorDataService.cs b/DigitalLearningSolutions.Data/DataServices/SelfAssessmentDataService/SelfAssessmentSupervisorDataService.cs
index 2a77222937..8d839f786a 100644
--- a/DigitalLearningSolutions.Data/DataServices/SelfAssessmentDataService/SelfAssessmentSupervisorDataService.cs
+++ b/DigitalLearningSolutions.Data/DataServices/SelfAssessmentDataService/SelfAssessmentSupervisorDataService.cs
@@ -110,6 +110,7 @@ int delegateUserId
return connection.Query(
@"SELECT DISTINCT
sd.ID AS SupervisorDelegateID,
+ au.AdminUserID AS SupervisorAdminUserID,
sd.SupervisorAdminID,
sd.SupervisorEmail,
sd.NotificationSent,
@@ -217,6 +218,7 @@ int delegateUserId
return connection.Query(
@"SELECT
AdminID,
+ AdminUserID,
Forename,
Surname,
Active,
diff --git a/DigitalLearningSolutions.Data/DataServices/SupervisorDataService.cs b/DigitalLearningSolutions.Data/DataServices/SupervisorDataService.cs
index bc31903d26..d0553153ee 100644
--- a/DigitalLearningSolutions.Data/DataServices/SupervisorDataService.cs
+++ b/DigitalLearningSolutions.Data/DataServices/SupervisorDataService.cs
@@ -1,7 +1,7 @@
namespace DigitalLearningSolutions.Data.DataServices
{
using Dapper;
- using DigitalLearningSolutions.Data.Models.RoleProfiles;
+ using DigitalLearningSolutions.Data.Models.CompetencyAssessments;
using DigitalLearningSolutions.Data.Models.SelfAssessments;
using DigitalLearningSolutions.Data.Models.Supervisor;
using Microsoft.Extensions.Logging;
@@ -23,8 +23,8 @@ public interface ISupervisorDataService
IEnumerable GetSupervisorDashboardToDoItemsForRequestedSignOffs(int adminId);
IEnumerable GetSupervisorDashboardToDoItemsForRequestedReviews(int adminId);
DelegateSelfAssessment? GetSelfAssessmentBaseByCandidateAssessmentId(int candidateAssessmentId, int? adminIdCategoryId);
- IEnumerable GetAvailableRoleProfilesForDelegate(int candidateId, int centreId, int? categoryId);
- RoleProfile? GetRoleProfileById(int selfAssessmentId);
+ IEnumerable GetAvailableCompetencyAssessmentsForDelegate(int candidateId, int centreId, int? categoryId);
+ CompetencyAssessment? GetCompetencyAssessmentById(int selfAssessmentId);
IEnumerable GetSupervisorRolesForSelfAssessment(int selfAssessmentId);
IEnumerable GetSupervisorRolesBySelfAssessmentIdForSupervisor(int selfAssessmentId);
IEnumerable GetDelegateNominatableSupervisorRolesForSelfAssessment(int selfAssessmentId);
@@ -105,16 +105,33 @@ INNER JOIN AdminAccounts AS aa
";
private const string delegateSelfAssessmentFields = "ca.ID, sa.ID AS SelfAssessmentID, sa.Name AS RoleName, sa.SupervisorSelfAssessmentReview, sa.SupervisorResultsReview, COALESCE (sasr.RoleName, 'Supervisor') AS SupervisorRoleTitle, ca.StartedDate";
- private const string signedOffFields = @"(SELECT TOP (1) casv.Verified
-FROM CandidateAssessmentSupervisorVerifications AS casv INNER JOIN
- CandidateAssessmentSupervisors AS cas ON casv.CandidateAssessmentSupervisorID = cas.ID
-WHERE(cas.CandidateAssessmentID = ca.ID) AND(casv.Requested IS NOT NULL) AND(casv.Verified IS NOT NULL)
-ORDER BY casv.Requested DESC) AS SignedOffDate,
-(SELECT TOP(1) casv.SignedOff
-FROM CandidateAssessmentSupervisorVerifications AS casv INNER JOIN
- CandidateAssessmentSupervisors AS cas ON casv.CandidateAssessmentSupervisorID = cas.ID
-WHERE(cas.CandidateAssessmentID = ca.ID) AND(casv.Requested IS NOT NULL) AND(casv.Verified IS NOT NULL)
-ORDER BY casv.Requested DESC) AS SignedOff,";
+ private const string signedOffFields = @"(SELECT CASE
+ WHEN MAX(sar.[DateTime]) >= MAX(casv.Verified) THEN NULL
+ ELSE MAX(casv.Verified)
+ END AS Verified
+ FROM CandidateAssessmentSupervisorVerifications AS casv INNER JOIN
+ CandidateAssessmentSupervisors AS cas ON casv.CandidateAssessmentSupervisorID = cas.ID INNER JOIN
+ CandidateAssessments AS ca1 ON cas.CandidateAssessmentID = ca1.ID LEFT JOIN
+ SelfAssessmentResults AS sar ON ca1.SelfAssessmentID = sar.SelfAssessmentID AND ca1.DelegateUserID = sar.DelegateUserID
+ WHERE(cas.CandidateAssessmentID = ca.ID) AND(casv.Requested IS NOT NULL) AND(casv.Verified IS NOT NULL)
+ GROUP BY ca1.ID ) AS SignedOffDate,
+
+ (SELECT CASE
+ WHEN MAX(sar.[DateTime]) >= MAX(casv.Verified) THEN NULL
+ ELSE (
+ SELECT TOP(1) casv.SignedOff
+ FROM CandidateAssessmentSupervisorVerifications AS casv INNER JOIN
+ CandidateAssessmentSupervisors AS cas ON casv.CandidateAssessmentSupervisorID = cas.ID
+ WHERE(cas.CandidateAssessmentID = ca.ID) AND(casv.Requested IS NOT NULL) AND(casv.Verified IS NOT NULL)
+ ORDER BY casv.Verified DESC
+ )
+ END AS SignedOff
+ FROM CandidateAssessmentSupervisorVerifications AS casv INNER JOIN
+ CandidateAssessmentSupervisors AS cas ON casv.CandidateAssessmentSupervisorID = cas.ID INNER JOIN
+ CandidateAssessments AS ca1 ON cas.CandidateAssessmentID = ca1.ID LEFT JOIN
+ SelfAssessmentResults AS sar ON ca1.SelfAssessmentID = sar.SelfAssessmentID AND ca1.DelegateUserID = sar.DelegateUserID
+ WHERE(cas.CandidateAssessmentID = ca.ID) AND(casv.Requested IS NOT NULL) AND(casv.Verified IS NOT NULL)
+ GROUP BY ca1.ID) AS SignedOff,";
public SupervisorDataService(IDbConnection connection, ILogger logger)
{
@@ -125,59 +142,63 @@ public SupervisorDataService(IDbConnection connection, ILogger(
- @"SELECT (SELECT COUNT(sd.ID) AS StaffCount
- FROM CustomPrompts AS cp6
- RIGHT OUTER JOIN CustomPrompts AS cp5
- RIGHT OUTER JOIN DelegateAccounts AS da
- RIGHT OUTER JOIN SupervisorDelegates AS sd
- INNER JOIN AdminUsers AS au
- ON sd.SupervisorAdminID = au.AdminID
- INNER JOIN Centres AS ct
- ON au.CentreID = ct.CentreID
- ON da.CentreID = ct.CentreID
- AND da.UserID = sd.DelegateUserID
- LEFT OUTER JOIN Users AS u
- LEFT OUTER JOIN JobGroups AS jg
- ON u.JobGroupID = jg.JobGroupID
- ON da.UserID = u.ID
- LEFT OUTER JOIN CustomPrompts AS cp1
- ON ct.CustomField1PromptID = cp1.CustomPromptID
- LEFT OUTER JOIN CustomPrompts AS cp2
- ON ct.CustomField2PromptID = cp2.CustomPromptID
- LEFT OUTER JOIN CustomPrompts AS cp3
- ON ct.CustomField3PromptID = cp3.CustomPromptID
- LEFT OUTER JOIN CustomPrompts AS cp4
- ON ct.CustomField4PromptID = cp4.CustomPromptID
- ON cp5.CustomPromptID = ct.CustomField5PromptID
- ON cp6.CustomPromptID = ct.CustomField6PromptID
- LEFT OUTER JOIN AdminAccounts AS au2
- ON da.UserID = au2.UserID AND da.CentreID = au2.CentreID
- WHERE (sd.SupervisorAdminID = @adminId) AND (sd.Removed IS NULL) AND
- (u.ID = da.UserID OR sd.DelegateUserID IS NULL)) AS StaffCount,
- (SELECT COUNT(ID) AS StaffCount
- FROM SupervisorDelegates AS SupervisorDelegates_1
- WHERE (SupervisorAdminID = @adminId)
- AND (DelegateUserID IS NULL)
- AND (Removed IS NULL)) AS StaffUnregisteredCount,
- (SELECT COUNT(ca.ID) AS ProfileSelfAssessmentCount
- FROM CandidateAssessmentSupervisors AS cas INNER JOIN
- CandidateAssessments AS ca ON cas.CandidateAssessmentID = ca.ID INNER JOIN
- SupervisorDelegates AS sd ON cas.SupervisorDelegateId = sd.ID
- WHERE (sd.SupervisorAdminID = @adminId) AND (cas.Removed IS NULL) AND ((ca.RemovedDate IS NULL))) AS ProfileSelfAssessmentCount,
- (SELECT COUNT(DISTINCT sa.ID) AS Expr1
- FROM SelfAssessments AS sa INNER JOIN
- CandidateAssessments AS ca ON sa.ID = ca.SelfAssessmentID LEFT OUTER JOIN
- SupervisorDelegates AS sd INNER JOIN
- CandidateAssessmentSupervisors AS cas ON sd.ID = cas.SupervisorDelegateId ON ca.ID = cas.CandidateAssessmentID
- WHERE (sd.SupervisorAdminID = @adminId)) As ProfileCount,
- COALESCE
- ((SELECT COUNT(casv.ID) AS Expr1
- FROM CandidateAssessmentSupervisors AS cas INNER JOIN
- CandidateAssessments AS ca ON cas.CandidateAssessmentID = ca.ID INNER JOIN
- SupervisorDelegates AS sd ON cas.SupervisorDelegateId = sd.ID INNER JOIN
- CandidateAssessmentSupervisorVerifications AS casv ON cas.ID = casv.CandidateAssessmentSupervisorID
- WHERE (sd.SupervisorAdminID = @adminId) AND ((ca.RemovedDate IS NULL) AND (cas.Removed IS NULL)) AND (casv.Verified IS NULL)
- ), 0) AS AwaitingReviewCount", new { adminId }
+ @"WITH BaseDelegates AS (
+ SELECT ID,DelegateUserID
+ FROM SupervisorDelegates
+ WHERE SupervisorAdminID = @adminId
+ AND Removed IS NULL
+ ),
+ StaffCounts AS (
+ SELECT
+ COUNT(*) AS StaffCount,
+ SUM(CASE WHEN DelegateUserID IS NULL THEN 1 ELSE 0 END) AS StaffUnregisteredCount
+ FROM BaseDelegates
+ ),
+ ProfileSelfAssessments AS (
+ SELECT COUNT(ca.ID) AS ProfileSelfAssessmentCount
+ FROM CandidateAssessmentSupervisors cas
+ INNER JOIN CandidateAssessments ca
+ ON cas.CandidateAssessmentID = ca.ID
+ INNER JOIN SupervisorDelegates sd
+ ON cas.SupervisorDelegateId = sd.ID
+ WHERE cas.Removed IS NULL
+ AND ca.RemovedDate IS NULL
+ AND sd.SupervisorAdminID = 10857
+ ),
+ ProfileCounts AS (
+ SELECT COUNT(DISTINCT sa.ID) AS ProfileCount
+ FROM SelfAssessments sa
+ JOIN CandidateAssessments ca
+ ON sa.ID = ca.SelfAssessmentID
+ JOIN CandidateAssessmentSupervisors cas
+ ON ca.ID = cas.CandidateAssessmentID
+ JOIN BaseDelegates sd
+ ON cas.SupervisorDelegateId = sd.ID
+ ),
+ AwaitingReviews AS (
+ SELECT COUNT(casv.ID) AS AwaitingReviewCount
+ FROM CandidateAssessmentSupervisors cas
+ JOIN CandidateAssessments ca
+ ON cas.CandidateAssessmentID = ca.ID
+ JOIN BaseDelegates sd
+ ON cas.SupervisorDelegateId = sd.ID
+ JOIN CandidateAssessmentSupervisorVerifications casv
+ ON cas.ID = casv.CandidateAssessmentSupervisorID
+ WHERE cas.Removed IS NULL
+ AND ca.RemovedDate IS NULL
+ AND casv.Verified IS NULL
+ )
+
+ SELECT
+ sc.StaffCount,
+ sc.StaffUnregisteredCount,
+ psa.ProfileSelfAssessmentCount,
+ pc.ProfileCount,
+ COALESCE(ar.AwaitingReviewCount, 0) AS AwaitingReviewCount
+ FROM StaffCounts sc
+ CROSS JOIN ProfileSelfAssessments psa
+ CROSS JOIN ProfileCounts pc
+ CROSS JOIN AwaitingReviews ar;", new { adminId }
).FirstOrDefault();
}
@@ -663,23 +684,43 @@ public IEnumerable GetSupervisorDashboardToDoItemsF
public IEnumerable GetSupervisorDashboardToDoItemsForRequestedReviews(int adminId)
{
return connection.Query(
- @"SELECT ca.ID, sd.ID AS SupervisorDelegateId, u.FirstName + ' ' + u.LastName AS DelegateName, sa.Name AS ProfileName, MAX(sasv.Requested) AS Requested, 0 AS SignOffRequest, 1 AS ResultsReviewRequest FROM CandidateAssessmentSupervisors AS cas INNER JOIN
- CandidateAssessments AS ca ON cas.CandidateAssessmentID = ca.ID INNER JOIN
- Users AS u ON ca.DelegateUserID = u.ID INNER JOIN
- SelfAssessments AS sa ON ca.SelfAssessmentID = sa.ID INNER JOIN
- SupervisorDelegates AS sd ON cas.SupervisorDelegateId = sd.ID INNER JOIN
- SelfAssessmentResults AS sar ON sar.SelfAssessmentID = sa.ID INNER JOIN
- Competencies AS co ON sar.CompetencyID = co.ID INNER JOIN
- SelfAssessmentResultSupervisorVerifications AS sasv ON sasv.SelfAssessmentResultId = sar.ID AND sasv.Superceded = 0
- AND sasv.CandidateAssessmentSupervisorID = cas.ID AND sar.DateTime = (
- SELECT TOP 1 sar2.DateTime
- FROM SelfAssessmentResults AS sar2
- WHERE sar2.ID = sar.ID AND sar2.SelfAssessmentID = sar.SelfAssessmentID AND sar2.CompetencyID = co.ID AND sar2.Result != 0 ORDER BY sar2.ID DESC
- ) INNER JOIN
- AdminAccounts AS aa ON sd.SupervisorAdminID = aa.ID
- WHERE (sd.SupervisorAdminID = @adminId) AND (cas.Removed IS NULL) AND (sasv.Verified IS NULL) AND (sd.Removed IS NULL)
- AND (aa.CategoryID is null or sa.CategoryID = aa.CategoryID)
- GROUP BY sa.ID, ca.ID, sd.ID, u.FirstName, u.LastName, sa.Name", new { adminId }
+ @"WITH FilteredSelfAssessmentResults AS (
+ SELECT
+ sar.ID,
+ sar.SelfAssessmentID,
+ sar.CompetencyID,
+ sar.DateTime
+ FROM SelfAssessmentResults sar
+ WHERE sar.Result <> 0
+ ),
+ BaseSupervisorDelegates AS (
+ SELECT ID, SupervisorAdminID
+ FROM SupervisorDelegates
+ WHERE SupervisorAdminID = @adminId
+ AND Removed IS NULL
+ ),
+ ValidCAS AS (
+ SELECT ID, CandidateAssessmentID, SupervisorDelegateId
+ FROM CandidateAssessmentSupervisors
+ WHERE Removed IS NULL
+ )
+ SELECT ca.ID,sd.ID AS SupervisorDelegateId, u.FirstName + ' ' + u.LastName AS DelegateName,
+ sa.Name AS ProfileName, MAX(sasv.Requested) AS Requested, 0 AS SignOffRequest, 1 AS ResultsReviewRequest
+ FROM ValidCAS cas
+ INNER JOIN CandidateAssessments ca
+ ON cas.CandidateAssessmentID = ca.ID
+ INNER JOIN Users u
+ ON ca.DelegateUserID = u.ID
+ INNER JOIN SelfAssessments sa ON ca.SelfAssessmentID = sa.ID
+ INNER JOIN BaseSupervisorDelegates sd ON cas.SupervisorDelegateId = sd.ID
+ INNER JOIN FilteredSelfAssessmentResults sar ON sar.SelfAssessmentID = sa.ID
+ INNER JOIN Competencies co ON sar.CompetencyID = co.ID
+ INNER JOIN SelfAssessmentResultSupervisorVerifications sasv
+ ON sasv.SelfAssessmentResultId = sar.ID AND sasv.Superceded = 0 AND sasv.CandidateAssessmentSupervisorID = cas.ID
+ INNER JOIN AdminAccounts aa ON sd.SupervisorAdminID = aa.ID
+ WHERE sasv.Verified IS NULL AND ca.RemovedDate IS NULL
+ AND (aa.CategoryID IS NULL OR sa.CategoryID = aa.CategoryID)
+ GROUP BY sa.ID, ca.ID, sd.ID, u.FirstName, u.LastName, sa.Name;", new { adminId }
);
}
@@ -755,10 +796,10 @@ public int RemoveSelfAssessmentResultSupervisorVerificationById(int id)
}
return numberOfAffectedRows;
}
- public IEnumerable GetAvailableRoleProfilesForDelegate(int delegateUserId, int centreId, int? categoryId)
+ public IEnumerable GetAvailableCompetencyAssessmentsForDelegate(int delegateUserId, int centreId, int? categoryId)
{
- return connection.Query(
- $@"SELECT rp.ID, rp.Name AS RoleProfileName, rp.Description, rp.BrandID, rp.ParentSelfAssessmentID, rp.[National], rp.[Public], rp.CreatedByAdminID AS OwnerAdminID, rp.NRPProfessionalGroupID, rp.NRPSubGroupID, rp.NRPRoleID, rp.PublishStatusID, 0 AS UserRole, rp.CreatedDate,
+ return connection.Query(
+ $@"SELECT rp.ID, rp.Name AS CompetencyAssessmentName, rp.Description, rp.BrandID, rp.ParentSelfAssessmentID, rp.[National], rp.[Public], rp.CreatedByAdminID AS OwnerAdminID, rp.NRPProfessionalGroupID, rp.NRPSubGroupID, rp.NRPRoleID, rp.PublishStatusID, 0 AS UserRole, rp.CreatedDate,
(SELECT BrandName
FROM Brands
WHERE (BrandID = rp.BrandID)) AS Brand, '' AS ParentSelfAssessment, '' AS Owner, rp.Archived, rp.LastEdit,
@@ -784,10 +825,10 @@ FROM CandidateAssessments AS CA
);
}
- public RoleProfile? GetRoleProfileById(int selfAssessmentId)
+ public CompetencyAssessment? GetCompetencyAssessmentById(int selfAssessmentId)
{
- return connection.Query(
- $@"SELECT ID, Name AS RoleProfileName, Description, BrandID, ParentSelfAssessmentID, [National], [Public], CreatedByAdminID AS OwnerAdminID, NRPProfessionalGroupID, NRPSubGroupID, NRPRoleID, PublishStatusID, 0 AS UserRole, CreatedDate,
+ return connection.Query(
+ $@"SELECT ID, Name AS CompetencyAssessmentName, Description, BrandID, ParentSelfAssessmentID, [National], [Public], CreatedByAdminID AS OwnerAdminID, NRPProfessionalGroupID, NRPSubGroupID, NRPRoleID, PublishStatusID, 0 AS UserRole, CreatedDate,
(SELECT BrandName
FROM Brands
WHERE (BrandID = rp.BrandID)) AS Brand,
@@ -876,14 +917,14 @@ FROM CandidateAssessments
@"UPDATE CandidateAssessments
SET DelegateUserID = @delegateUserId,
SelfAssessmentID = @selfAssessmentId,
- CompleteByDate = NULL,
+ CompleteByDate = @completeByDate,
EnrolmentMethodId = 2,
EnrolledByAdminId = @adminId,
CentreID = @centreId,
RemovedDate = NULL,
NonReportable = CASE WHEN NonReportable = 1 THEN NonReportable ELSE @isLoggedInUser END
WHERE ID = @existingCandidateAssessmentId",
- new { delegateUserId, selfAssessmentId, adminId, centreId, existingCandidateAssessmentId, isLoggedInUser });
+ new { delegateUserId, selfAssessmentId, adminId, centreId, existingCandidateAssessmentId, isLoggedInUser, completeByDate });
if (numberOfAffectedRows < 1)
{
diff --git a/DigitalLearningSolutions.Data/Enums/NavMenuTab.cs b/DigitalLearningSolutions.Data/Enums/NavMenuTab.cs
index a0d60c8399..7691f565e9 100644
--- a/DigitalLearningSolutions.Data/Enums/NavMenuTab.cs
+++ b/DigitalLearningSolutions.Data/Enums/NavMenuTab.cs
@@ -91,9 +91,9 @@ public class NavMenuTab : Enumeration
nameof(Frameworks)
);
- public static readonly NavMenuTab RolesProfiles = new NavMenuTab(
+ public static readonly NavMenuTab CompetencyAssessments = new NavMenuTab(
17,
- nameof(RolesProfiles)
+ nameof(CompetencyAssessments)
);
public static readonly NavMenuTab LogOut = new NavMenuTab(
diff --git a/DigitalLearningSolutions.Data/Extensions/ConfigurationExtensions.cs b/DigitalLearningSolutions.Data/Extensions/ConfigurationExtensions.cs
index f869df5f74..61dc157340 100644
--- a/DigitalLearningSolutions.Data/Extensions/ConfigurationExtensions.cs
+++ b/DigitalLearningSolutions.Data/Extensions/ConfigurationExtensions.cs
@@ -60,6 +60,8 @@ public static class ConfigurationExtensions
private const string TableauViewName = "ViewName";
private const string TableauSiteName = "SiteName";
private const string TableauAuthApi = "AuthApiPath";
+ private const string SupervisorDefaultText = "SupervisorDefaultText";
+ private const string LearnerDefaultText = "LearnerDefaultText";
public static string GetAppRootPath(this IConfiguration config)
{
return config[AppRootPathName]!;
@@ -268,5 +270,13 @@ public static string GetTableauViewName(this IConfiguration config)
{
return config[$"{TableauSectionKey}:{TableauViewName}"]!;
}
+ public static string GetSupervisorDefaultText(this IConfiguration config)
+ {
+ return config[SupervisorDefaultText]!;
+ }
+ public static string GetLearnerDefaultText(this IConfiguration config)
+ {
+ return config[LearnerDefaultText]!;
+ }
}
}
diff --git a/DigitalLearningSolutions.Data/Models/Common/Users/Administrator.cs b/DigitalLearningSolutions.Data/Models/Common/Users/Administrator.cs
index aca9575264..8cb1d20ee0 100644
--- a/DigitalLearningSolutions.Data/Models/Common/Users/Administrator.cs
+++ b/DigitalLearningSolutions.Data/Models/Common/Users/Administrator.cs
@@ -6,6 +6,7 @@ namespace DigitalLearningSolutions.Data.Models.Common.Users
public class Administrator : BaseSearchableItem
{
public int AdminID { get; set; }
+ public int AdminUserID { get; set; }
public int CentreID { get; set; }
public string? Email { get; set; }
public string? Forename { get; set; }
diff --git a/DigitalLearningSolutions.Data/Models/CompetencyAssessments/Competency.cs b/DigitalLearningSolutions.Data/Models/CompetencyAssessments/Competency.cs
new file mode 100644
index 0000000000..0640c403cd
--- /dev/null
+++ b/DigitalLearningSolutions.Data/Models/CompetencyAssessments/Competency.cs
@@ -0,0 +1,20 @@
+using DigitalLearningSolutions.Data.Models.Frameworks;
+using System.Collections.Generic;
+
+namespace DigitalLearningSolutions.Data.Models.CompetencyAssessments
+{
+ public class Competency
+ {
+ public int StructureId { get; set; }
+ public int CompetencyID { get; set; }
+ public int FrameworkId { get; set; }
+ public string? FrameworkName { get; set; }
+ public string? GroupName { get; set; }
+ public int GroupId { get; set; }
+ public string? CompetencyName { get; set; }
+ public string? CompetencyDescription { get; set; }
+ public bool Optional { get; set; }
+ public bool GroupOptionalCompetencies { get; set; }
+ public IEnumerable CompetencyFlags { get; set; } = [];
+ }
+}
diff --git a/DigitalLearningSolutions.Data/Models/RoleProfiles/RoleProfile.cs b/DigitalLearningSolutions.Data/Models/CompetencyAssessments/CompetencyAssessment.cs
similarity index 53%
rename from DigitalLearningSolutions.Data/Models/RoleProfiles/RoleProfile.cs
rename to DigitalLearningSolutions.Data/Models/CompetencyAssessments/CompetencyAssessment.cs
index ec1970fc92..b8c2fe8b2a 100644
--- a/DigitalLearningSolutions.Data/Models/RoleProfiles/RoleProfile.cs
+++ b/DigitalLearningSolutions.Data/Models/CompetencyAssessments/CompetencyAssessment.cs
@@ -1,8 +1,7 @@
-namespace DigitalLearningSolutions.Data.Models.RoleProfiles
+namespace DigitalLearningSolutions.Data.Models.CompetencyAssessments
{
using System;
- using System.ComponentModel.DataAnnotations;
- public class RoleProfile : RoleProfileBase
+ public class CompetencyAssessment : CompetencyAssessmentBase
{
public DateTime CreatedDate { get; set; }
public string? Brand
@@ -10,15 +9,27 @@ public string? Brand
get => brand;
set => brand = GetValidOrNull(value);
}
- public string? ParentRoleProfile { get; set; }
+ public string? Category
+ {
+ get => category;
+ set => category = GetValidOrNull(value);
+ }
+ public string? ParentCompetencyAssessment { get; set; }
public string? Owner { get; set; }
public DateTime? Archived { get; set; }
public DateTime LastEdit { get; set; }
+ public string LinkedFrameworks
+ {
+ get => linkedFrameworks ?? "None";
+ set => linkedFrameworks = value;
+ }
public string? NRPProfessionalGroup { get; set; }
public string? NRPSubGroup { get; set; }
public string? NRPRole { get; set; }
- public int? RoleProfileReviewID { get; set; }
+ public int? CompetencyAssessmentReviewID { get; set; }
private string? brand;
+ private string? category;
+ private string? linkedFrameworks;
private static string? GetValidOrNull(string? toValidate)
{
return toValidate != null && toValidate.ToLower() == "undefined" ? null : toValidate;
diff --git a/DigitalLearningSolutions.Data/Models/CompetencyAssessments/CompetencyAssessmentBase.cs b/DigitalLearningSolutions.Data/Models/CompetencyAssessments/CompetencyAssessmentBase.cs
new file mode 100644
index 0000000000..58a11866d7
--- /dev/null
+++ b/DigitalLearningSolutions.Data/Models/CompetencyAssessments/CompetencyAssessmentBase.cs
@@ -0,0 +1,41 @@
+namespace DigitalLearningSolutions.Data.Models.CompetencyAssessments
+{
+ using System.ComponentModel.DataAnnotations;
+ public class CompetencyAssessmentBase
+ {
+ public int ID { get; set; }
+ [StringLength(255, MinimumLength = 3)]
+ [Required]
+ public string CompetencyAssessmentName { get; set; } = string.Empty;
+ public string? Description { get; set; }
+ public int BrandID { get; set; }
+ public int CategoryID { get; set; }
+ public int? ParentCompetencyAssessmentID { get; set; }
+ public bool National { get; set; }
+ public bool Public { get; set; }
+ public int OwnerAdminID { get; set; }
+ public int? NRPProfessionalGroupID { get; set; }
+ public int? NRPSubGroupID { get; set; }
+ public int? NRPRoleID { get; set; }
+ public int PublishStatusID { get; set; }
+ public int UserRole { get; set; }
+ public string? Vocabulary { get; set; }
+ public bool EnforceRoleRequirementsForSignOff { get; set; }
+ public bool IncludeRequirementsFilters { get; set; }
+ public int? MinimumOptionalCompetencies { get; set; }
+ public string? ManageOptionalCompetenciesPrompt { get; set; }
+ public bool IncludeLearnerDeclarationPrompt { get; set; }
+ public bool IncludesSignposting { get; set; }
+ public bool LinearNavigation { get; set; }
+ public bool UseDescriptionExpanders { get; set; }
+ public string? QuestionLabel { get; set; }
+ public string? ReviewerCommentsLabel { get; set; }
+ public bool SupervisorSelfAssessmentReview { get; set; }
+ public bool SupervisorResultsReview { get; set; }
+ public string? SignOffRequestorStatement { get; set; }
+ public string? SignOffSupervisorStatement { get; set; }
+ public int? SelfAssessmentReviewID { get; set; }
+ public int? SelfAssessmentCommentID { get; set; }
+ }
+}
+
diff --git a/DigitalLearningSolutions.Data/Models/CompetencyAssessments/CompetencyAssessmentCollaborator.cs b/DigitalLearningSolutions.Data/Models/CompetencyAssessments/CompetencyAssessmentCollaborator.cs
new file mode 100644
index 0000000000..9e58b3199e
--- /dev/null
+++ b/DigitalLearningSolutions.Data/Models/CompetencyAssessments/CompetencyAssessmentCollaborator.cs
@@ -0,0 +1,10 @@
+namespace DigitalLearningSolutions.Data.Models.CompetencyAssessments
+{
+ public class CompetencyAssessmentCollaborator
+ {
+ public int ID { get; set; }
+ public int SelfAssessmentID { get; set; }
+ public int? AdminID { get; set; }
+ public bool CanModify { get; set; }
+ }
+}
diff --git a/DigitalLearningSolutions.Data/Models/CompetencyAssessments/CompetencyAssessmentCollaboratorDetail.cs b/DigitalLearningSolutions.Data/Models/CompetencyAssessments/CompetencyAssessmentCollaboratorDetail.cs
new file mode 100644
index 0000000000..35a88d3bbc
--- /dev/null
+++ b/DigitalLearningSolutions.Data/Models/CompetencyAssessments/CompetencyAssessmentCollaboratorDetail.cs
@@ -0,0 +1,11 @@
+namespace DigitalLearningSolutions.Data.Models.CompetencyAssessments
+{
+ public class CompetencyAssessmentCollaboratorDetail : CompetencyAssessmentCollaborator
+ {
+ public string? UserEmail { get; set; }
+ public bool? UserActive { get; set; }
+ public string? CompetencyAssessmentRole { get; set; }
+ public int? SelfAssessmentReviewID { get; set; }
+ public bool SignOffRequired { get; set; }
+ }
+}
diff --git a/DigitalLearningSolutions.Data/Models/CompetencyAssessments/CompetencyAssessmentCollaboratorNotification.cs b/DigitalLearningSolutions.Data/Models/CompetencyAssessments/CompetencyAssessmentCollaboratorNotification.cs
new file mode 100644
index 0000000000..f1578157cf
--- /dev/null
+++ b/DigitalLearningSolutions.Data/Models/CompetencyAssessments/CompetencyAssessmentCollaboratorNotification.cs
@@ -0,0 +1,9 @@
+namespace DigitalLearningSolutions.Data.Models.CompetencyAssessments
+{
+ public class CompetencyAssessmentCollaboratorNotification : CompetencyAssessmentCollaboratorDetail
+ {
+ public string InvitedByEmail { get; set; } = string.Empty;
+ public string InvitedByName { get; set; } = string.Empty;
+ public string CompetencyAssessmentName { get; set; } = string.Empty;
+ }
+}
diff --git a/DigitalLearningSolutions.Data/Models/CompetencyAssessments/CompetencyAssessmentFeatures.cs b/DigitalLearningSolutions.Data/Models/CompetencyAssessments/CompetencyAssessmentFeatures.cs
new file mode 100644
index 0000000000..f68bfe7a67
--- /dev/null
+++ b/DigitalLearningSolutions.Data/Models/CompetencyAssessments/CompetencyAssessmentFeatures.cs
@@ -0,0 +1,20 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace DigitalLearningSolutions.Data.Models.CompetencyAssessments
+{
+ public class CompetencyAssessmentFeatures
+ {
+ public int ID { get; set; }
+ public string CompetencyAssessmentName { get; set; } = string.Empty;
+ public int UserRole { get; set; }
+ public bool DescriptionStatus { get; set; }
+ public bool ProviderandCategoryStatus { get; set; }
+ public bool VocabularyStatus { get; set; }
+ public bool WorkingGroupStatus { get; set; }
+ public bool AllframeworkCompetenciesStatus { get; set; }
+ }
+}
diff --git a/DigitalLearningSolutions.Data/Models/CompetencyAssessments/CompetencyAssessmentTaskStatus.cs b/DigitalLearningSolutions.Data/Models/CompetencyAssessments/CompetencyAssessmentTaskStatus.cs
new file mode 100644
index 0000000000..654220f151
--- /dev/null
+++ b/DigitalLearningSolutions.Data/Models/CompetencyAssessments/CompetencyAssessmentTaskStatus.cs
@@ -0,0 +1,20 @@
+namespace DigitalLearningSolutions.Data.Models.CompetencyAssessments
+{
+ public class CompetencyAssessmentTaskStatus
+ {
+ public int Id { get; set; }
+ public bool? IntroductoryTextTaskStatus { get; set; }
+ public bool? BrandingTaskStatus { get; set; }
+ public bool? VocabularyTaskStatus { get; set; }
+ public bool? WorkingGroupTaskStatus { get; set; }
+ public bool? NationalRoleProfileTaskStatus { get; set; }
+ public bool? FrameworkLinksTaskStatus { get; set; }
+ public bool? SelectCompetenciesTaskStatus { get; set; }
+ public bool? OptionalCompetenciesTaskStatus { get; set; }
+ public bool? RoleRequirementsTaskStatus { get; set; }
+ public bool? SupervisorRolesTaskStatus { get; set; }
+ public bool? SelfAssessmentOptionsTaskStatus { get; set; }
+ public bool? ReviewTaskStatus { get; set; }
+
+ }
+}
diff --git a/DigitalLearningSolutions.Data/Models/CompetencyAssessments/CompetencyWithAssessmentQuestionRoleRequirements.cs b/DigitalLearningSolutions.Data/Models/CompetencyAssessments/CompetencyWithAssessmentQuestionRoleRequirements.cs
new file mode 100644
index 0000000000..0cf1dcedc1
--- /dev/null
+++ b/DigitalLearningSolutions.Data/Models/CompetencyAssessments/CompetencyWithAssessmentQuestionRoleRequirements.cs
@@ -0,0 +1,19 @@
+namespace DigitalLearningSolutions.Data.Models.CompetencyAssessments
+{
+ public class CompetencyWithAssessmentQuestionRoleRequirements
+ {
+ public int? CompetencyGroupID { get; set; }
+ public string? GroupName { get; set; }
+ public int CompetencyID { get; set; }
+ public string? Competency { get; set; }
+ public string? CompetencyDescription { get; set; }
+ public bool? Optional { get; set; }
+ public int AssessmentQuestionID { get; set; }
+ public string? Question { get; set; }
+ public string? InputTypeName { get; set; }
+ public bool Required { get; set; }
+ public int? ResponseValue { get; set; }
+ public string? Response { get; set; }
+ public int? LevelRAG { get; set; }
+ }
+}
diff --git a/DigitalLearningSolutions.Data/Models/CompetencyAssessments/LinkedFrameworks.cs b/DigitalLearningSolutions.Data/Models/CompetencyAssessments/LinkedFrameworks.cs
new file mode 100644
index 0000000000..f410bab903
--- /dev/null
+++ b/DigitalLearningSolutions.Data/Models/CompetencyAssessments/LinkedFrameworks.cs
@@ -0,0 +1,9 @@
+namespace DigitalLearningSolutions.Data.Models.CompetencyAssessments
+{
+ using DigitalLearningSolutions.Data.Models.Frameworks;
+ public class LinkedFramework : BaseFramework
+ {
+ public int AssessmentFrameworkCompetencyCount { get; set; } = 0;
+ public bool IsPrimary { get; set; }
+ }
+}
diff --git a/DigitalLearningSolutions.Data/Models/RoleProfiles/NRPProfessionalGroups.cs b/DigitalLearningSolutions.Data/Models/CompetencyAssessments/NRPProfessionalGroups.cs
similarity index 81%
rename from DigitalLearningSolutions.Data/Models/RoleProfiles/NRPProfessionalGroups.cs
rename to DigitalLearningSolutions.Data/Models/CompetencyAssessments/NRPProfessionalGroups.cs
index de1f9e4588..bdc4f6942a 100644
--- a/DigitalLearningSolutions.Data/Models/RoleProfiles/NRPProfessionalGroups.cs
+++ b/DigitalLearningSolutions.Data/Models/CompetencyAssessments/NRPProfessionalGroups.cs
@@ -1,4 +1,4 @@
-namespace DigitalLearningSolutions.Data.Models.RoleProfiles
+namespace DigitalLearningSolutions.Data.Models.CompetencyAssessments
{
using System.ComponentModel.DataAnnotations;
public class NRPProfessionalGroups
@@ -10,3 +10,4 @@ public class NRPProfessionalGroups
public bool Active { get; set; }
}
}
+
diff --git a/DigitalLearningSolutions.Data/Models/RoleProfiles/NRPRoles.cs b/DigitalLearningSolutions.Data/Models/CompetencyAssessments/NRPRoles.cs
similarity index 67%
rename from DigitalLearningSolutions.Data/Models/RoleProfiles/NRPRoles.cs
rename to DigitalLearningSolutions.Data/Models/CompetencyAssessments/NRPRoles.cs
index f404bfb243..22cba2ada8 100644
--- a/DigitalLearningSolutions.Data/Models/RoleProfiles/NRPRoles.cs
+++ b/DigitalLearningSolutions.Data/Models/CompetencyAssessments/NRPRoles.cs
@@ -1,4 +1,4 @@
-namespace DigitalLearningSolutions.Data.Models.RoleProfiles
+namespace DigitalLearningSolutions.Data.Models.CompetencyAssessments
{
using System.ComponentModel.DataAnnotations;
public class NRPRoles
@@ -7,7 +7,7 @@ public class NRPRoles
public int NRPSubGroupID { get; set; }
[StringLength(255, MinimumLength = 3)]
[Required]
- public string RoleProfile { get; set; } = string.Empty;
+ public string ProfileName { get; set; } = string.Empty;
public bool Active { get; set; }
}
}
diff --git a/DigitalLearningSolutions.Data/Models/RoleProfiles/NRPSubGroups.cs b/DigitalLearningSolutions.Data/Models/CompetencyAssessments/NRPSubGroups.cs
similarity index 71%
rename from DigitalLearningSolutions.Data/Models/RoleProfiles/NRPSubGroups.cs
rename to DigitalLearningSolutions.Data/Models/CompetencyAssessments/NRPSubGroups.cs
index 6d43b936a2..f100a6b2e2 100644
--- a/DigitalLearningSolutions.Data/Models/RoleProfiles/NRPSubGroups.cs
+++ b/DigitalLearningSolutions.Data/Models/CompetencyAssessments/NRPSubGroups.cs
@@ -1,10 +1,9 @@
-namespace DigitalLearningSolutions.Data.Models.RoleProfiles
+namespace DigitalLearningSolutions.Data.Models.CompetencyAssessments
{
using System.ComponentModel.DataAnnotations;
public class NRPSubGroups
{
public int ID { get; set; }
- public int NRPProfessionalGroupID { get; set; }
[StringLength(255, MinimumLength = 3)]
[Required]
public string SubGroup { get; set; } = string.Empty;
diff --git a/DigitalLearningSolutions.Data/Models/CompetencyAssessments/SelfAssessmentReview.cs b/DigitalLearningSolutions.Data/Models/CompetencyAssessments/SelfAssessmentReview.cs
new file mode 100644
index 0000000000..9d9919b0d1
--- /dev/null
+++ b/DigitalLearningSolutions.Data/Models/CompetencyAssessments/SelfAssessmentReview.cs
@@ -0,0 +1,18 @@
+using System;
+namespace DigitalLearningSolutions.Data.Models.CompetencyAssessments
+{
+ public class SelfAssessmentReview
+ {
+ public int ID { get; set; }
+ public int SelfAssessmentID { get; set; }
+ public int SelfAssessmentCollaboratorID { get; set; }
+ public string UserEmail { get; set; } = string.Empty;
+ public bool IsRegistered { get; set; }
+ public DateTime ReviewRequested { get; set; }
+ public DateTime? ReviewComplete { get; set; }
+ public bool SignedOff { get; set; }
+ public int? SelfAssessmentCommentID { get; set; }
+ public string? Comment { get; set; }
+ public bool SignOffRequired { get; set; }
+ }
+}
diff --git a/DigitalLearningSolutions.Data/Models/CompetencyAssessments/SelfAssessmentReviewOutcomeNotification.cs b/DigitalLearningSolutions.Data/Models/CompetencyAssessments/SelfAssessmentReviewOutcomeNotification.cs
new file mode 100644
index 0000000000..7babb86aaf
--- /dev/null
+++ b/DigitalLearningSolutions.Data/Models/CompetencyAssessments/SelfAssessmentReviewOutcomeNotification.cs
@@ -0,0 +1,12 @@
+namespace DigitalLearningSolutions.Data.Models.CompetencyAssessments
+{
+ public class SelfAssessmentReviewOutcomeNotification : SelfAssessmentReview
+ {
+ public string SelfAssessmentName { get; set; } = string.Empty;
+ public string? OwnerEmail { get; set; }
+ public string? OwnerFirstName { get; set; }
+ public string? ReviewerFirstName { get; set; }
+ public string? ReviewerLastName { get; set; }
+ public bool? ReviewerActive { get; set; }
+ }
+}
diff --git a/DigitalLearningSolutions.Data/Models/Frameworks/BaseFramework.cs b/DigitalLearningSolutions.Data/Models/Frameworks/BaseFramework.cs
index 0291d3d13f..38adaf1cb8 100644
--- a/DigitalLearningSolutions.Data/Models/Frameworks/BaseFramework.cs
+++ b/DigitalLearningSolutions.Data/Models/Frameworks/BaseFramework.cs
@@ -22,7 +22,6 @@ public class BaseFramework : BaseSearchableItem
public string? UpdatedBy { get; set; }
public int UserRole { get; set; }
public int? FrameworkReviewID { get; set; }
-
public override string SearchableName
{
get => SearchableNameOverrideForFuzzySharp ?? FrameworkName;
diff --git a/DigitalLearningSolutions.Data/Models/Frameworks/BrandedFramework.cs b/DigitalLearningSolutions.Data/Models/Frameworks/BrandedFramework.cs
index f49b526831..5ba38617ac 100644
--- a/DigitalLearningSolutions.Data/Models/Frameworks/BrandedFramework.cs
+++ b/DigitalLearningSolutions.Data/Models/Frameworks/BrandedFramework.cs
@@ -2,6 +2,8 @@
{
public class BrandedFramework : BaseFramework
{
+ public string? Description { get; set; }
+ public string? Vocabulary { get; set; }
public string? Brand
{
get => brand;
diff --git a/DigitalLearningSolutions.Data/Models/Frameworks/DashboardData.cs b/DigitalLearningSolutions.Data/Models/Frameworks/DashboardData.cs
index f5d28bd284..207e61079c 100644
--- a/DigitalLearningSolutions.Data/Models/Frameworks/DashboardData.cs
+++ b/DigitalLearningSolutions.Data/Models/Frameworks/DashboardData.cs
@@ -4,7 +4,7 @@ public class DashboardData
{
public int FrameworksCount { get; set; }
public int MyFrameworksCount { get; set; }
- public int RoleProfileCount { get; set; }
- public int MyRoleProfileCount { get; set; }
+ public int CompetencyAssessmentCount { get; set; }
+ public int MyCompetencyAssessmentCount { get; set; }
}
}
diff --git a/DigitalLearningSolutions.Data/Models/Frameworks/DashboardToDoItem.cs b/DigitalLearningSolutions.Data/Models/Frameworks/DashboardToDoItem.cs
index 96ee4e8326..dc7e6b0386 100644
--- a/DigitalLearningSolutions.Data/Models/Frameworks/DashboardToDoItem.cs
+++ b/DigitalLearningSolutions.Data/Models/Frameworks/DashboardToDoItem.cs
@@ -4,7 +4,7 @@
public class DashboardToDoItem
{
public int? FrameworkID { get; set; }
- public int? RoleProfileID { get; set; }
+ public int? CompetencyAssessmentID { get; set; }
public string ItemName { get; set; } = string.Empty;
public string RequestorName { get; set; } = string.Empty;
public bool SignOffRequired { get; set; }
diff --git a/DigitalLearningSolutions.Data/Models/Frameworks/FrameworkCompetency.cs b/DigitalLearningSolutions.Data/Models/Frameworks/FrameworkCompetency.cs
index a42d3d10fe..cf2117457c 100644
--- a/DigitalLearningSolutions.Data/Models/Frameworks/FrameworkCompetency.cs
+++ b/DigitalLearningSolutions.Data/Models/Frameworks/FrameworkCompetency.cs
@@ -1,6 +1,7 @@
namespace DigitalLearningSolutions.Data.Models.Frameworks
{
using DigitalLearningSolutions.Data.Models.SearchSortFilterPaginate;
+ using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
public class FrameworkCompetency : BaseSearchableItem
{
@@ -15,6 +16,7 @@ public class FrameworkCompetency : BaseSearchableItem
public int CompetencyLearningResourcesCount { get; set; }
public string? FrameworkName { get; set; }
public bool? AlwaysShowDescription { get; set; }
+ public IEnumerable CompetencyFlags { get; set; } = new List();
public override string SearchableName
{
diff --git a/DigitalLearningSolutions.Data/Models/RoleProfiles/RoleProfileBase.cs b/DigitalLearningSolutions.Data/Models/RoleProfiles/RoleProfileBase.cs
deleted file mode 100644
index 1c843fae46..0000000000
--- a/DigitalLearningSolutions.Data/Models/RoleProfiles/RoleProfileBase.cs
+++ /dev/null
@@ -1,24 +0,0 @@
-namespace DigitalLearningSolutions.Data.Models.RoleProfiles
-{
- using System;
- using System.ComponentModel.DataAnnotations;
- public class RoleProfileBase
- {
- public int ID { get; set; }
- [StringLength(255, MinimumLength = 3)]
- [Required]
- public string RoleProfileName { get; set; } = string.Empty;
- public string? Description { get; set; }
- public int BrandID { get; set; }
- public int? ParentRoleProfileID { get; set; }
- public bool National { get; set; }
- public bool Public { get; set; }
- public int OwnerAdminID { get; set; }
- public int? NRPProfessionalGroupID { get; set; }
- public int? NRPSubGroupID { get; set; }
- public int? NRPRoleID { get; set; }
- public int PublishStatusID { get; set; }
- public int UserRole { get; set; }
-
- }
-}
diff --git a/DigitalLearningSolutions.Data/Models/SelfAssessments/SelfAssessment.cs b/DigitalLearningSolutions.Data/Models/SelfAssessments/SelfAssessment.cs
index dbd1da0de6..4fcf6363e2 100644
--- a/DigitalLearningSolutions.Data/Models/SelfAssessments/SelfAssessment.cs
+++ b/DigitalLearningSolutions.Data/Models/SelfAssessments/SelfAssessment.cs
@@ -17,6 +17,7 @@ public class SelfAssessment : CurrentLearningItem
public DateTime? EnrolmentCutoffDate { get; set; }
public string? RetirementReason { get; set; }
+ public bool IncludeLearnerDeclarationPrompt { get; set; }
}
}
diff --git a/DigitalLearningSolutions.Data/Models/SelfAssessments/SelfAssessmentSupervisor.cs b/DigitalLearningSolutions.Data/Models/SelfAssessments/SelfAssessmentSupervisor.cs
index 89ef4e61b5..1814e544d7 100644
--- a/DigitalLearningSolutions.Data/Models/SelfAssessments/SelfAssessmentSupervisor.cs
+++ b/DigitalLearningSolutions.Data/Models/SelfAssessments/SelfAssessmentSupervisor.cs
@@ -9,6 +9,7 @@ public class SelfAssessmentSupervisor
public int ID { get; set; }
public int SupervisorDelegateID { get; set; }
public int? SupervisorAdminID { get; set; }
+ public int? SupervisorAdminUserID { get; set; }
public string SupervisorName { get; set; } = string.Empty;
public string SupervisorEmail { get; set; } = string.Empty;
public DateTime NotificationSent { get; set; }
@@ -20,5 +21,5 @@ public class SelfAssessmentSupervisor
public string CentreName { get; set; } = string.Empty;
public bool AllowDelegateNomination { get; set; }
public bool AllowSupervisorRoleSelection { get; set; }
-}
+ }
}
diff --git a/DigitalLearningSolutions.Data/Models/SessionData/CompetencyAssessments/SessionNewCompetencyAssessment.cs b/DigitalLearningSolutions.Data/Models/SessionData/CompetencyAssessments/SessionNewCompetencyAssessment.cs
new file mode 100644
index 0000000000..b8d572404f
--- /dev/null
+++ b/DigitalLearningSolutions.Data/Models/SessionData/CompetencyAssessments/SessionNewCompetencyAssessment.cs
@@ -0,0 +1,15 @@
+namespace DigitalLearningSolutions.Data.Models.SessionData.CompetencyAssessments
+{
+ using System;
+ using DigitalLearningSolutions.Data.Models.CompetencyAssessments;
+ public class SessionNewCompetencyAssessment
+ {
+ public SessionNewCompetencyAssessment()
+ {
+ Id = new Guid();
+ CompetencyAssessmentBase = new CompetencyAssessmentBase();
+ }
+ public Guid Id { get; set; }
+ public CompetencyAssessmentBase CompetencyAssessmentBase { get; set; }
+ }
+}
diff --git a/DigitalLearningSolutions.Data/Models/SessionData/RoleProfiles/SessionNewRoleProfile.cs b/DigitalLearningSolutions.Data/Models/SessionData/RoleProfiles/SessionNewRoleProfile.cs
deleted file mode 100644
index 617c8ebb64..0000000000
--- a/DigitalLearningSolutions.Data/Models/SessionData/RoleProfiles/SessionNewRoleProfile.cs
+++ /dev/null
@@ -1,15 +0,0 @@
-namespace DigitalLearningSolutions.Data.Models.SessionData.RoleProfiles
-{
- using System;
- using DigitalLearningSolutions.Data.Models.RoleProfiles;
- public class SessionNewRoleProfile
- {
- public SessionNewRoleProfile()
- {
- Id = new Guid();
- RoleProfileBase = new RoleProfileBase();
- }
- public Guid Id { get; set; }
- public RoleProfileBase RoleProfileBase { get; set; }
- }
-}
diff --git a/DigitalLearningSolutions.Data/Models/SessionData/SelfAssessments/SessionAddSupervisor.cs b/DigitalLearningSolutions.Data/Models/SessionData/SelfAssessments/SessionAddSupervisor.cs
index af9629f071..398a55d3d0 100644
--- a/DigitalLearningSolutions.Data/Models/SessionData/SelfAssessments/SessionAddSupervisor.cs
+++ b/DigitalLearningSolutions.Data/Models/SessionData/SelfAssessments/SessionAddSupervisor.cs
@@ -8,5 +8,6 @@ public class SessionAddSupervisor
public string? SupervisorEmail { get; set; }
public int? SelfAssessmentSupervisorRoleId { get; set; }
public int? CentreID { get; set; }
+ public bool NonReportable { get; set; }
}
}
diff --git a/DigitalLearningSolutions.Data/Models/SessionData/Supervisor/SessionEnrolOnRoleProfile.cs b/DigitalLearningSolutions.Data/Models/SessionData/Supervisor/SessionEnrolOnCompetencyAssessment.cs
similarity index 86%
rename from DigitalLearningSolutions.Data/Models/SessionData/Supervisor/SessionEnrolOnRoleProfile.cs
rename to DigitalLearningSolutions.Data/Models/SessionData/Supervisor/SessionEnrolOnCompetencyAssessment.cs
index 6d2f3e777a..a3271d569d 100644
--- a/DigitalLearningSolutions.Data/Models/SessionData/Supervisor/SessionEnrolOnRoleProfile.cs
+++ b/DigitalLearningSolutions.Data/Models/SessionData/Supervisor/SessionEnrolOnCompetencyAssessment.cs
@@ -1,7 +1,7 @@
namespace DigitalLearningSolutions.Data.Models.SessionData.Supervisor
{
using System;
- public class SessionEnrolOnRoleProfile
+ public class SessionEnrolOnCompetencyAssessment
{
public int? SelfAssessmentID { get; set; }
public DateTime? CompleteByDate { get; set; }
diff --git a/DigitalLearningSolutions.Data/Models/Supervisor/DelegateSelfAssessment.cs b/DigitalLearningSolutions.Data/Models/Supervisor/DelegateSelfAssessment.cs
index 69da5a36fd..212089f305 100644
--- a/DigitalLearningSolutions.Data/Models/Supervisor/DelegateSelfAssessment.cs
+++ b/DigitalLearningSolutions.Data/Models/Supervisor/DelegateSelfAssessment.cs
@@ -22,7 +22,7 @@ public class DelegateSelfAssessment
public string? DescriptionLabel { get; set; }
public string? ReviewerCommentsLabel { get; set; }
public string? SubGroup { get; set; }
- public string? RoleProfile { get; set; }
+ public string? CompetencyAssessment { get; set; }
public int SignOffRequested { get; set; }
public int ResultsVerificationRequests { get; set; }
public bool IsSupervisorResultsReviewed { get; set; }
diff --git a/DigitalLearningSolutions.Web.Tests/Controllers/Register/RegisterDelegateByCentreControllerTests.cs b/DigitalLearningSolutions.Web.Tests/Controllers/Register/RegisterDelegateByCentreControllerTests.cs
index 0d4a77fc61..0040c2b514 100644
--- a/DigitalLearningSolutions.Web.Tests/Controllers/Register/RegisterDelegateByCentreControllerTests.cs
+++ b/DigitalLearningSolutions.Web.Tests/Controllers/Register/RegisterDelegateByCentreControllerTests.cs
@@ -176,7 +176,7 @@ public void LearnerInformationPost_updates_tempdata_correctly()
const string answer4 = "answer4";
const string answer5 = "answer5";
const string answer6 = "answer6";
- const string professionalRegistrationNumber = "PRN1234";
+ const string professionalRegistrationNumber = "PR123456";
var model = new LearnerInformationViewModel
{
JobGroup = jobGroupId,
diff --git a/DigitalLearningSolutions.Web.Tests/Controllers/TrackingSystem/Delegates/EditDelegateControllerTests.cs b/DigitalLearningSolutions.Web.Tests/Controllers/TrackingSystem/Delegates/EditDelegateControllerTests.cs
index 5c5df8ac44..c08f3dbe35 100644
--- a/DigitalLearningSolutions.Web.Tests/Controllers/TrackingSystem/Delegates/EditDelegateControllerTests.cs
+++ b/DigitalLearningSolutions.Web.Tests/Controllers/TrackingSystem/Delegates/EditDelegateControllerTests.cs
@@ -175,7 +175,7 @@ public void Index_post_returns_view_with_model_error_with_invalid_prn()
result.As().Model.Should().BeOfType();
AssertModelStateErrorIsExpected(
result,
- "Invalid professional registration number format - Only alphanumeric characters (a-z, A-Z and 0-9) and hyphens (-) allowed"
+ ErrorMessagesTestHelper.InvalidFormatError
);
A.CallTo(() => userService.GetDelegateById(A._)).MustNotHaveHappened();
}
diff --git a/DigitalLearningSolutions.Web.Tests/Helpers/ProfessionalRegistrationNumberHelperTests.cs b/DigitalLearningSolutions.Web.Tests/Helpers/ProfessionalRegistrationNumberHelperTests.cs
index d78c74f042..a7270f280d 100644
--- a/DigitalLearningSolutions.Web.Tests/Helpers/ProfessionalRegistrationNumberHelperTests.cs
+++ b/DigitalLearningSolutions.Web.Tests/Helpers/ProfessionalRegistrationNumberHelperTests.cs
@@ -1,11 +1,12 @@
namespace DigitalLearningSolutions.Web.Tests.Helpers
{
- using System.Linq;
using DigitalLearningSolutions.Web.Helpers;
+ using DigitalLearningSolutions.Web.Tests.TestHelpers;
using FluentAssertions;
using FluentAssertions.Execution;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using NUnit.Framework;
+ using System.Linq;
public class ProfessionalRegistrationNumberHelperTests
{
@@ -70,7 +71,7 @@ public void ValidateProfessionalRegistrationNumber_does_not_set_errors_when_vali
{
// Given
var state = new ModelStateDictionary();
- const string validPrn = "abc-123";
+ const string validPrn = "AB123456";
// When
ProfessionalRegistrationNumberHelper.ValidateProfessionalRegistrationNumber(
@@ -104,22 +105,13 @@ public void ValidateProfessionalRegistrationNumber_sets_error_when_hasPrn_is_not
}
}
- [TestCase(null, "Enter a professional registration number")]
- [TestCase("", "Enter a professional registration number")]
- [TestCase("123", "Professional registration number must be between 5 and 20 characters")]
- [TestCase("0123456789-0123456789", "Professional registration number must be between 5 and 20 characters")]
- [TestCase(
- "01234_",
- "Invalid professional registration number format - Only alphanumeric characters (a-z, A-Z and 0-9) and hyphens (-) allowed"
- )]
- [TestCase(
- "01234 ",
- "Invalid professional registration number format - Only alphanumeric characters (a-z, A-Z and 0-9) and hyphens (-) allowed"
- )]
- [TestCase(
- "01234$",
- "Invalid professional registration number format - Only alphanumeric characters (a-z, A-Z and 0-9) and hyphens (-) allowed"
- )]
+ [TestCase(null, ErrorMessagesTestHelper.MissingNumberError)]
+ [TestCase("", ErrorMessagesTestHelper.MissingNumberError)]
+ [TestCase("1234", ErrorMessagesTestHelper.LengthError)]
+ [TestCase("1234", ErrorMessagesTestHelper.LengthError)]
+ [TestCase("01234_", ErrorMessagesTestHelper.InvalidFormatError)]
+ [TestCase("01234 ", ErrorMessagesTestHelper.InvalidFormatError)]
+ [TestCase("01234$", ErrorMessagesTestHelper.InvalidFormatError)]
public void ValidateProfessionalRegistrationNumber_sets_error_when_prn_is_invalid(
string prn,
string expectedError
diff --git a/DigitalLearningSolutions.Web.Tests/Services/GroupServiceTests/GroupsServiceEnrolDelegateTests.cs b/DigitalLearningSolutions.Web.Tests/Services/GroupServiceTests/GroupsServiceEnrolDelegateTests.cs
index fa333c2d7a..9f2f83dde4 100644
--- a/DigitalLearningSolutions.Web.Tests/Services/GroupServiceTests/GroupsServiceEnrolDelegateTests.cs
+++ b/DigitalLearningSolutions.Web.Tests/Services/GroupServiceTests/GroupsServiceEnrolDelegateTests.cs
@@ -111,7 +111,7 @@ public void EnrolDelegateOnGroupCourses_adds_new_progress_record_when_existing_p
}
[Test]
- public void EnrolDelegateOnGroupCourses_adds_new_progress_record_when_existing_progress_record_is_completed()
+ public void EnrolDelegateOnGroupCourses_does_not_create_new_progress_record_when_existing_progress_record_is_completed()
{
// Given
var existingProgressRecord = ProgressTestHelper.GetDefaultProgress(completed: testDate);
@@ -135,22 +135,25 @@ public void EnrolDelegateOnGroupCourses_adds_new_progress_record_when_existing_p
using (new AssertionScope())
{
DelegateProgressRecordMustNotHaveBeenUpdated();
- A.CallTo(
- () => progressDataService.CreateNewDelegateProgress(
- reusableDelegateDetails.Id,
- reusableGroupCourse.CustomisationId,
- reusableGroupCourse.CurrentVersion,
- null,
- 3,
- null,
- A._,
- A._,
- testDate
- )
- ).MustHaveHappened();
- A.CallTo(() => progressDataService.CreateNewAspProgress(GenericRelatedTutorialId, GenericNewProgressId))
- .MustHaveHappened();
+
+ A.CallTo(() => progressDataService.CreateNewDelegateProgress(
+ A._,
+ A._,
+ A._,
+ A._,
+ A._,
+ A._,
+ A._,
+ A._,
+ A._
+ )).MustNotHaveHappened();
+
+ A.CallTo(() => progressDataService.CreateNewAspProgress(
+ A._,
+ A._
+ )).MustNotHaveHappened();
}
+
}
[Test]
diff --git a/DigitalLearningSolutions.Web.Tests/TestHelpers/ErrorMessagesTestHelper.cs b/DigitalLearningSolutions.Web.Tests/TestHelpers/ErrorMessagesTestHelper.cs
new file mode 100644
index 0000000000..7636944e7a
--- /dev/null
+++ b/DigitalLearningSolutions.Web.Tests/TestHelpers/ErrorMessagesTestHelper.cs
@@ -0,0 +1,17 @@
+
+namespace DigitalLearningSolutions.Web.Tests.TestHelpers
+{
+ public static class ErrorMessagesTestHelper
+ {
+ public const string InvalidFormatError =
+ "Invalid professional registration number format. " +
+ "Valid formats include: 7 digits (e.g., 1234567), 1–2 letters followed by 6 digits (e.g., AB123456), " +
+ "4–8 digits, an optional 'P' plus 5–6 digits, 'C' or 'P' plus 6 digits, " +
+ "an optional letter plus 5–6 digits, 'L' plus 4–6 digits, " +
+ "or 2 digits followed by a hyphen and 4–5 alphanumeric characters (e.g., 12-AB123).";
+
+ public const string MissingNumberError = "Enter a professional registration number";
+ public const string LengthError = "Professional registration number must be between 5 and 20 characters";
+
+ }
+}
diff --git a/DigitalLearningSolutions.Web.Tests/TestHelpers/SelfAssessmentTestHelper.cs b/DigitalLearningSolutions.Web.Tests/TestHelpers/SelfAssessmentTestHelper.cs
index 937fb6d7fb..b5849bebce 100644
--- a/DigitalLearningSolutions.Web.Tests/TestHelpers/SelfAssessmentTestHelper.cs
+++ b/DigitalLearningSolutions.Web.Tests/TestHelpers/SelfAssessmentTestHelper.cs
@@ -27,7 +27,8 @@ public static CurrentSelfAssessment CreateDefaultSelfAssessment(
bool useDescriptionExpanders = true,
string vocabulary = "Capability",
string verificationRoleName = "Supervisor",
- string signOffRoleName = "Supervisor"
+ string signOffRoleName = "Supervisor",
+ bool includeLearnerDeclarationPrompt = true
)
{
return new CurrentSelfAssessment
@@ -47,6 +48,7 @@ public static CurrentSelfAssessment CreateDefaultSelfAssessment(
Vocabulary = vocabulary,
VerificationRoleName = verificationRoleName,
SignOffRoleName = signOffRoleName,
+ IncludeLearnerDeclarationPrompt = includeLearnerDeclarationPrompt
};
}
diff --git a/DigitalLearningSolutions.Web.Tests/TestHelpers/SupervisorTagTestHelper.cs b/DigitalLearningSolutions.Web.Tests/TestHelpers/SupervisorTagTestHelper.cs
index d2d1077141..5344c99af0 100644
--- a/DigitalLearningSolutions.Web.Tests/TestHelpers/SupervisorTagTestHelper.cs
+++ b/DigitalLearningSolutions.Web.Tests/TestHelpers/SupervisorTagTestHelper.cs
@@ -101,10 +101,10 @@ public static DelegateSelfAssessment CreateDefaultDelegateSelfAssessment(
string? descriptionLabel = null,
string? reviewerCommentsLabel = null,
string? subGroup = null,
- string? roleProfile = null,
- int signOffRequested = 1,
- int resultsVerificationRequests = 1,
- bool isSupervisorResultsReviewed = false,
+ string? competencyAssessment = null,
+ int signOffRequested =1,
+ int resultsVerificationRequests =1,
+ bool isSupervisorResultsReviewed =false,
bool isAssignedToSupervisor = false,
bool nonReportable = false
)
@@ -117,7 +117,7 @@ public static DelegateSelfAssessment CreateDefaultDelegateSelfAssessment(
ResultsVerificationRequests = resultsVerificationRequests,
ReviewerCommentsLabel = reviewerCommentsLabel,
SubGroup = subGroup,
- RoleProfile = roleProfile,
+ CompetencyAssessment = competencyAssessment,
SignOffRequested = signOffRequested,
SupervisorResultsReview = supervisorResultsReview,
SupervisorSelfAssessmentReview = supervisorSelfAssessmentReview,
diff --git a/DigitalLearningSolutions.Web/Controllers/CompetencyAssessmentsController/CompetencyAssessments.cs b/DigitalLearningSolutions.Web/Controllers/CompetencyAssessmentsController/CompetencyAssessments.cs
new file mode 100644
index 0000000000..9357c697ae
--- /dev/null
+++ b/DigitalLearningSolutions.Web/Controllers/CompetencyAssessmentsController/CompetencyAssessments.cs
@@ -0,0 +1,1787 @@
+namespace DigitalLearningSolutions.Web.Controllers.CompetencyAssessmentsController
+{
+ using DigitalLearningSolutions.Data.Enums;
+ using DigitalLearningSolutions.Data.Extensions;
+ using DigitalLearningSolutions.Data.Models.CompetencyAssessments;
+ using DigitalLearningSolutions.Data.Models.Frameworks;
+ using DigitalLearningSolutions.Data.Models.SelfAssessments;
+ using DigitalLearningSolutions.Web.Attributes;
+ using DigitalLearningSolutions.Web.Helpers;
+ using DigitalLearningSolutions.Web.Models.Enums;
+ using DigitalLearningSolutions.Web.ViewModels.CompetencyAssessments;
+ using DigitalLearningSolutions.Web.ViewModels.LearningPortal.SelfAssessments;
+ using DocumentFormat.OpenXml.EMMA;
+ using GDS.MultiPageFormData.Enums;
+ using Microsoft.AspNetCore.Mvc;
+ using Microsoft.AspNetCore.Mvc.Rendering;
+ using Microsoft.Extensions.Logging;
+ using Serilog.Extensions.Hosting;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Threading.Tasks;
+
+ public partial class CompetencyAssessmentsController
+ {
+ [Route("/CompetencyAssessments/View/{tabname}/{page=1:int}")]
+ [SetSelectedTab(nameof(NavMenuTab.CompetencyAssessments))]
+ public IActionResult ViewCompetencyAssessments(string tabname, string? searchString = null,
+ string sortBy = CompetencyAssessmentSortByOptionTexts.CompetencyAssessmentName,
+ string sortDirection = BaseCompetencyAssessmentsPageViewModel.AscendingText,
+ int page = 1
+ )
+ {
+ var adminId = GetAdminID();
+ var isWorkforceManager = GetIsWorkforceManager() | GetIsFrameworkDeveloper();
+ var isWorkforceContributor = GetIsWorkforceContributor() | GetIsFrameworkContributor();
+ IEnumerable competencyAssessments;
+ if (tabname == "All")
+ {
+ competencyAssessments = competencyAssessmentService.GetAllCompetencyAssessments(adminId);
+ }
+ else
+ {
+ if (!isWorkforceContributor && !isWorkforceManager)
+ {
+ return RedirectToAction("ViewCompetencyAssessments", "CompetencyAssessments", new { tabname = "All" });
+ }
+ competencyAssessments = competencyAssessmentService.GetCompetencyAssessmentsForAdminId(adminId);
+ }
+ if (competencyAssessments == null)
+ {
+ logger.LogWarning($"Attempt to display competency assessments for admin {adminId} returned null.");
+ return StatusCode(403);
+ }
+ MyCompetencyAssessmentsViewModel myCompetencyAssessments;
+ AllCompetencyAssessmentsViewModel allCompetencyAssessments;
+ if (tabname == "Mine")
+ {
+ myCompetencyAssessments = new MyCompetencyAssessmentsViewModel(
+ competencyAssessments,
+ searchString,
+ sortBy,
+ sortDirection,
+ page,
+ isWorkforceManager);
+ allCompetencyAssessments = new AllCompetencyAssessmentsViewModel(
+ new List(),
+ searchString,
+ sortBy,
+ sortDirection,
+ page
+ );
+ }
+ else
+ {
+ allCompetencyAssessments = new AllCompetencyAssessmentsViewModel(
+ competencyAssessments,
+ searchString,
+ sortBy,
+ sortDirection,
+ page);
+ myCompetencyAssessments = new MyCompetencyAssessmentsViewModel(
+ new List(),
+ searchString,
+ sortBy,
+ sortDirection,
+ page,
+ isWorkforceManager
+ );
+ }
+ var currentTab = tabname == "All" ? CompetencyAssessmentsTab.AllCompetencyAssessments : CompetencyAssessmentsTab.MyCompetencyAssessments;
+ CompetencyAssessmentsViewModel? model = new CompetencyAssessmentsViewModel(
+ isWorkforceManager,
+ isWorkforceContributor,
+ allCompetencyAssessments,
+ myCompetencyAssessments,
+ currentTab
+ );
+ return View("Index", model);
+ }
+
+ [Route("/CompetencyAssessments/{actionName}/Name/{competencyAssessmentId}")]
+ [Route("/CompetencyAssessments/Framework/{frameworkId}/{actionName}/Name")]
+ [Route("/CompetencyAssessments/Framework/{frameworkId}/{competencyAssessmentId}/{actionName}/Name")]
+ [Route("/CompetencyAssessments/{actionName}/Name")]
+ [SetSelectedTab(nameof(NavMenuTab.CompetencyAssessments))]
+ public IActionResult CompetencyAssessmentName(string actionName, int competencyAssessmentId = 0, int? frameworkId = null)
+ {
+ var adminId = GetAdminID();
+ var competencyAssessmentBase = new CompetencyAssessmentBase();
+ if ((frameworkId.HasValue && frameworkId.Value != 0 && actionName == "New"))
+ {
+ var data = new CompetencyAssessmentFeaturesViewModel();
+ SetcompetencyAssessmentFeaturesData(data);
+ }
+ if (competencyAssessmentId > 0)
+ {
+ competencyAssessmentBase = competencyAssessmentService.GetCompetencyAssessmentBaseById(competencyAssessmentId, adminId);
+ var result = ValidateCompetencyAssessmentAndRole(competencyAssessmentId, adminId, "Competency Assessment Name", competencyAssessmentBase);
+ if (result.StatusCode != 200)
+ return result;
+ }
+ else if (frameworkId != null)
+ {
+ var framework = frameworkService.GetBaseFrameworkByFrameworkId((int)frameworkId, adminId);
+ if (framework != null)
+ {
+ competencyAssessmentBase.CompetencyAssessmentName = framework.FrameworkName;
+ }
+ }
+ return View("Name", competencyAssessmentBase);
+ }
+
+ [HttpPost]
+ [Route("/CompetencyAssessments/{actionName}/Name/{competencyAssessmentId}")]
+ [Route("/CompetencyAssessments/Framework/{frameworkId}/{actionName}/Name")]
+ [Route("/CompetencyAssessments/Framework/{frameworkId}/{competencyAssessmentId}/{actionName}/Name")]
+ [Route("/CompetencyAssessments/{actionName}/Name")]
+ [SetSelectedTab(nameof(NavMenuTab.CompetencyAssessments))]
+ public IActionResult SaveProfileName(CompetencyAssessmentBase competencyAssessmentBase, string actionName, int competencyAssessmentId = 0, int? frameworkId = null)
+ {
+ if (!ModelState.IsValid)
+ {
+ ModelState.Remove(nameof(CompetencyAssessmentBase.CompetencyAssessmentName));
+ ModelState.AddModelError(nameof(CompetencyAssessmentBase.CompetencyAssessmentName), "Please enter a valid competency assessment name (between 3 and 255 characters)");
+ return View("Name", competencyAssessmentBase);
+ }
+ else
+ {
+ var userCentreId = (int)GetCentreId();
+ var adminId = GetAdminID();
+ if (actionName == "New")
+ {
+ var sameItems = competencyAssessmentService.GetCompetencyAssessmentBaseByName(competencyAssessmentBase.CompetencyAssessmentName.Trim(), GetAdminID());
+ if (sameItems != null)
+ {
+ ModelState.Remove(nameof(CompetencyAssessmentBase.CompetencyAssessmentName));
+ ModelState.AddModelError(nameof(CompetencyAssessmentBase.CompetencyAssessmentName), "Another competency assessment exists with that name. Please choose a different name.");
+ return View("Name", competencyAssessmentBase);
+ }
+ competencyAssessmentId = competencyAssessmentService.InsertCompetencyAssessment(adminId, userCentreId, competencyAssessmentBase.CompetencyAssessmentName.Trim(), frameworkId);
+ if (frameworkId.HasValue && frameworkId.Value != 0) return RedirectToAction("CompetencyAssessmentFeatures", new { competencyAssessmentId, frameworkId });
+ }
+ else
+ {
+
+ var isUpdated = competencyAssessmentService.UpdateCompetencyAssessmentName(competencyAssessmentBase.ID, adminId, competencyAssessmentBase.CompetencyAssessmentName.Trim());
+ if (!isUpdated)
+ {
+ ModelState.AddModelError(nameof(CompetencyAssessmentBase.CompetencyAssessmentName), "Another competency assessment exists with that name. Please choose a different name.");
+ return View("Name", competencyAssessmentBase);
+ }
+ if (frameworkId.HasValue && frameworkId.Value != 0
+ && competencyAssessmentId != 0
+ && actionName == "Edit") return RedirectToAction("CompetencyAssessmentFeatures", new { competencyAssessmentId, frameworkId });
+ }
+ return RedirectToAction("ManageCompetencyAssessment", new { competencyAssessmentId, frameworkId });
+ }
+ }
+ [Route("/CompetencyAssessments/Framework/{frameworkId}/{competencyAssessmentId}/Manage")]
+ [Route("/CompetencyAssessments/{competencyAssessmentId}/Manage")]
+ public IActionResult ManageCompetencyAssessment(int competencyAssessmentId, int? frameworkId = null)
+ {
+ var adminId = GetAdminID();
+ var competencyAssessmentBase = competencyAssessmentService.GetCompetencyAssessmentBaseById(competencyAssessmentId, adminId);
+ var result = ValidateCompetencyAssessmentAndRole(competencyAssessmentId, adminId, "Manage Competency Assessment", competencyAssessmentBase);
+ if (result.StatusCode != 200)
+ return result;
+
+ bool hasCompetencies = competencyAssessmentService.GetCompetenciesForCompetencyAssessment(competencyAssessmentId).Any();
+ var competencyAssessmentTaskStatus = competencyAssessmentService.GetCompetencyAssessmentTaskStatus(competencyAssessmentId, frameworkId);
+ var model = new ManageCompetencyAssessmentViewModel(competencyAssessmentBase, competencyAssessmentTaskStatus, hasCompetencies);
+ return View("ManageCompetencyAssessment", model);
+ }
+
+ [Route("/CompetencyAssessments/{competencyAssessmentId}/NationalRoleProfileLinks/{actionName}")]
+ [SetSelectedTab(nameof(NavMenuTab.CompetencyAssessments))]
+ public IActionResult EditRoleProfileLinks(int competencyAssessmentId = 0, string actionName = "EditGroup")
+ {
+ var adminId = GetAdminID();
+ CompetencyAssessmentBase? competencyAssessmentBase;
+ if (competencyAssessmentId > 0)
+ {
+ competencyAssessmentBase = competencyAssessmentService.GetCompetencyAssessmentBaseById(competencyAssessmentId, adminId);
+ var result = ValidateCompetencyAssessmentAndRole(competencyAssessmentId, adminId, "National Role Profile Links", competencyAssessmentBase);
+ if (result.StatusCode != 200)
+ return result;
+ }
+ else
+ {
+ competencyAssessmentBase = competencyAssessmentService.GetCompetencyAssessmentBaseById(competencyAssessmentId, adminId);
+ }
+ var professionalGroups = competencyAssessmentService.GetNRPProfessionalGroups();
+ var subGroups = competencyAssessmentService.GetNRPSubGroups(competencyAssessmentBase.NRPProfessionalGroupID);
+ var roles = competencyAssessmentService.GetNRPRoles(competencyAssessmentBase.NRPSubGroupID);
+ var competencyAssessmentTaskStatus = competencyAssessmentService.GetCompetencyAssessmentTaskStatus(competencyAssessmentId, null);
+ var model = new EditRoleProfileLinksViewModel(competencyAssessmentBase, professionalGroups, subGroups, roles, actionName, competencyAssessmentTaskStatus.NationalRoleProfileTaskStatus);
+ return View(model);
+ }
+
+ [HttpPost]
+ [Route("/CompetencyAssessments/{competencyAssessmentId}/NationalRoleProfileLinks/{actionName}")]
+ public IActionResult EditRoleProfileLinks(EditRoleProfileLinksViewModel model, string actionName, int competencyAssessmentId = 0)
+ {
+ var adminId = GetAdminID();
+ var competencyAssessmentBase = competencyAssessmentService.GetCompetencyAssessmentBaseById(competencyAssessmentId, adminId);
+ competencyAssessmentService.UpdateRoleProfileLinksTaskStatus(model.ID, model.TaskStatus ?? false);
+ var result = ValidateCompetencyAssessmentAndRole(competencyAssessmentId, adminId, "Edit National Role Profile Links", competencyAssessmentBase);
+ if (result.StatusCode != 200)
+ return result;
+ if (competencyAssessmentBase.NRPProfessionalGroupID != model.ProfessionalGroupId)
+ {
+ model.SubGroupId = null;
+ model.RoleId = null;
+ }
+ if (competencyAssessmentBase.NRPSubGroupID != model.SubGroupId)
+ {
+ model.RoleId = null;
+ }
+ var isUpdated = competencyAssessmentService.UpdateCompetencyRoleProfileLinks(model.ID, adminId, model.ProfessionalGroupId, model.SubGroupId, model.RoleId);
+ if (model.ActionName == "EditGroup")
+ {
+ if (model.ProfessionalGroupId == null)
+ {
+ return RedirectToAction("EditRoleProfileLinks", new { actionName = "Summary", competencyAssessmentId });
+ }
+ else
+ {
+ return RedirectToAction("EditRoleProfileLinks", new { actionName = "EditSubGroup", competencyAssessmentId });
+ }
+ }
+ else if (model.ActionName == "EditSubGroup")
+ {
+ if (model.SubGroupId == null)
+ {
+ return RedirectToAction("EditRoleProfileLinks", new { actionName = "Summary", competencyAssessmentId });
+ }
+ else
+ {
+ return RedirectToAction("EditRoleProfileLinks", new { actionName = "EditRole", competencyAssessmentId });
+ }
+ }
+ else if (model.ActionName == "EditRole")
+ {
+ return RedirectToAction("EditRoleProfileLinks", new { actionName = "Summary", competencyAssessmentId });
+ }
+ else
+ {
+ return RedirectToAction("ManageCompetencyAssessment", new { competencyAssessmentId });
+ }
+ }
+
+ [Route("/CompetencyAssessments/SubGroup/{actionName}/{competencyAssessmentId}")]
+ [Route("/CompetencyAssessments/SubGroup/{actionName}")]
+ [SetSelectedTab(nameof(NavMenuTab.CompetencyAssessments))]
+ public IActionResult CompetencyAssessmentSubGroup(string actionName, int competencyAssessmentId = 0)
+ {
+ return View("SubGroup");
+ }
+
+ [Route("/CompetencyAssessments/{competencyAssessmentId}/Description/")]
+ public IActionResult EditDescription(int competencyAssessmentId)
+ {
+ var adminId = GetAdminID();
+ var competencyAssessmentBase = competencyAssessmentService.GetCompetencyAssessmentBaseById(competencyAssessmentId, adminId);
+ var result = ValidateCompetencyAssessmentAndRole(competencyAssessmentId, adminId, "Edit Description", competencyAssessmentBase);
+ if (result.StatusCode != 200)
+ return result;
+ var competencyAssessmentTaskStatus = competencyAssessmentService.GetCompetencyAssessmentTaskStatus(competencyAssessmentId, null);
+ var model = new EditDescriptionViewModel(competencyAssessmentBase, competencyAssessmentTaskStatus.IntroductoryTextTaskStatus);
+ return View(model);
+ }
+ [HttpPost]
+ [Route("/CompetencyAssessments/{competencyAssessmentId}/Description/")]
+ public IActionResult SaveDescription(EditDescriptionViewModel model)
+ {
+ if (string.IsNullOrWhiteSpace(StringHelper.StripHtmlTags(model.Description)))
+ {
+ ModelState.AddModelError(nameof(EditDescriptionViewModel.Description), "Please enter introductory text");
+ }
+ if (!ModelState.IsValid)
+ {
+ return View("EditDescription", model);
+ }
+ var adminId = GetAdminID();
+ var isUpdated = competencyAssessmentService.UpdateCompetencyAssessmentDescription(model.ID, adminId, SanitizerHelper.SanitizeHtmlData(model.Description));
+ competencyAssessmentService.UpdateIntroductoryTextTaskStatus(model.ID, model.TaskStatus ?? false);
+ return RedirectToAction("ManageCompetencyAssessment", new { competencyAssessmentId = model.ID });
+ }
+ [Route("/CompetencyAssessments/{competencyAssessmentId}/Branding/")]
+ public IActionResult EditBranding(int competencyAssessmentId)
+ {
+ var centreId = GetCentreId();
+ var adminId = GetAdminID();
+ var brandsList = commonService.GetBrandListForCentre((int)centreId).Select(b => new { b.BrandID, b.BrandName }).ToList();
+ var categoryList = commonService.GetCategoryListForCentre((int)centreId).Select(c => new { c.CourseCategoryID, c.CategoryName }).ToList();
+ var brandSelectList = new SelectList(brandsList, "BrandID", "BrandName");
+ var categorySelectList = new SelectList(categoryList, "CourseCategoryID", "CategoryName");
+ var competencyAssessment = competencyAssessmentService.GetCompetencyAssessmentById(competencyAssessmentId, adminId);
+ var competencyAssessmentTaskStatus = competencyAssessmentService.GetCompetencyAssessmentTaskStatus(competencyAssessmentId, null);
+ var model = new EditBrandingViewModel(competencyAssessment, brandSelectList, categorySelectList, competencyAssessmentTaskStatus.BrandingTaskStatus);
+ return View(model);
+ }
+ [HttpPost]
+ [Route("/CompetencyAssessments/{competencyAssessmentId}/Branding/")]
+ public IActionResult EditBranding(EditBrandingViewModel model)
+ {
+ var adminId = GetAdminID();
+ var centreId = GetCentreId();
+ if (!ModelState.IsValid)
+ {
+ var brandsList = commonService.GetBrandListForCentre((int)centreId).Select(b => new { b.BrandID, b.BrandName }).ToList();
+ var categoryList = commonService.GetCategoryListForCentre((int)centreId).Select(c => new { c.CourseCategoryID, c.CategoryName }).ToList();
+ var brandSelectList = new SelectList(brandsList, "BrandID", "BrandName");
+ var categorySelectList = new SelectList(categoryList, "CourseCategoryID", "CategoryName");
+ model.BrandSelectList = brandSelectList;
+ model.CategorySelectList = categorySelectList;
+ return View("EditBranding", model);
+ }
+ if (model.BrandID == 0)
+ {
+ model.BrandID = commonService.InsertBrandAndReturnId(model.Brand, (int)centreId);
+ }
+ if (model.CategoryID == 0)
+ {
+ model.CategoryID = commonService.InsertCategoryAndReturnId(model.Category, (int)centreId);
+ }
+ var isUpdated = competencyAssessmentService.UpdateCompetencyAssessmentBranding(model.ID, adminId, model.BrandID, model.CategoryID);
+ competencyAssessmentService.UpdateBrandingTaskStatus(model.ID, model.TaskStatus ?? false);
+ return RedirectToAction("ManageCompetencyAssessment", new { competencyAssessmentId = model.ID });
+ }
+ [Route("/CompetencyAssessments/{competencyAssessmentId}/Vocabulary/")]
+ public IActionResult EditVocabulary(int competencyAssessmentId)
+ {
+ var adminId = GetAdminID();
+ var competencyAssessmentBase = competencyAssessmentService.GetCompetencyAssessmentBaseById(competencyAssessmentId, adminId);
+ var result = ValidateCompetencyAssessmentAndRole(competencyAssessmentId, adminId, "Edit Vocabulary", competencyAssessmentBase);
+ if (result.StatusCode != 200)
+ return result;
+ var competencyAssessmentTaskStatus = competencyAssessmentService.GetCompetencyAssessmentTaskStatus(competencyAssessmentId, null);
+ var model = new EditVocabularyViewModel(competencyAssessmentBase, competencyAssessmentTaskStatus.VocabularyTaskStatus);
+ return View(model);
+ }
+ [HttpPost]
+ [Route("/CompetencyAssessments/{competencyAssessmentId}/Vocabulary/")]
+ public IActionResult SaveVocabulary(EditVocabularyViewModel model)
+ {
+ if (!ModelState.IsValid)
+ {
+ return View("EditVocabulary", model);
+ }
+ var adminId = GetAdminID();
+ var isUpdated = competencyAssessmentService.UpdateCompetencyAssessmentVocabulary(model.ID, adminId, model.Vocabulary);
+ competencyAssessmentService.UpdateVocabularyTaskStatus(model.ID, model.TaskStatus ?? false);
+ return RedirectToAction("ManageCompetencyAssessment", new { competencyAssessmentId = model.ID });
+ }
+ [Route("/CompetencyAssessments/{competencyAssessmentId}/Frameworks/{actionName}")]
+ public IActionResult SelectFrameworkSources(int competencyAssessmentId, string actionName)
+ {
+ var adminId = GetAdminID();
+ var frameworks = frameworkService.GetAllFrameworks(adminId);
+ var competencyAssessmentBase = competencyAssessmentService.GetCompetencyAssessmentBaseById(competencyAssessmentId, adminId);
+ if (competencyAssessmentBase == null)
+ {
+ logger.LogWarning($"Failed to load Vocabulary page for competencyAssessmentId: {competencyAssessmentId} adminId: {adminId}");
+ return StatusCode(500);
+ }
+ var result = ValidateCompetencyAssessmentAndRole(competencyAssessmentId, adminId, "Select Framework Sources", competencyAssessmentBase);
+ if (result.StatusCode != 200)
+ return result;
+ var primaryFrameworkId = competencyAssessmentService.GetPrimaryLinkedFrameworkId(competencyAssessmentId);
+ var additionalFrameworks = competencyAssessmentService.GetLinkedFrameworkIds(competencyAssessmentId);
+ var competencyAssessmentTaskStatus = competencyAssessmentService.GetCompetencyAssessmentTaskStatus(competencyAssessmentId, null);
+ var model = new SelectFrameworkSourcesViewModel(competencyAssessmentBase, frameworks, additionalFrameworks, primaryFrameworkId, competencyAssessmentTaskStatus.FrameworkLinksTaskStatus, actionName);
+ return View(model);
+ }
+ [HttpPost]
+ [Route("/CompetencyAssessments/{competencyAssessmentId}/Frameworks/{actionName}")]
+ public IActionResult SelectFrameworkSources(SelectFrameworkSourcesFormData model, string actionName)
+ {
+ var adminId = GetAdminID();
+ var competencyAssessmentId = model.CompetencyAssessmentId;
+ if (!ModelState.IsValid)
+ {
+ var frameworks = frameworkService.GetAllFrameworks(adminId);
+ var competencyAssessmentBase = competencyAssessmentService.GetCompetencyAssessmentBaseById(competencyAssessmentId, adminId);
+
+ if (competencyAssessmentBase == null)
+ {
+ logger.LogWarning($"Failed to load Vocabulary page for competencyAssessmentId: {competencyAssessmentId} adminId: {adminId}");
+ return StatusCode(500);
+ }
+
+ if (competencyAssessmentBase.UserRole < 2)
+ {
+ return StatusCode(403);
+ }
+
+ var primaryFrameworkId = competencyAssessmentService.GetPrimaryLinkedFrameworkId(competencyAssessmentId);
+ var additionalFrameworks = competencyAssessmentService.GetLinkedFrameworkIds(competencyAssessmentId);
+
+ var viewModel = new SelectFrameworkSourcesViewModel(
+ competencyAssessmentBase,
+ frameworks,
+ additionalFrameworks,
+ primaryFrameworkId,
+ model.TaskStatus,
+ model.ActionName
+ );
+
+ return View("SelectFrameworkSources", viewModel);
+ }
+
+ if (actionName == "AddFramework")
+ {
+ competencyAssessmentService.InsertSelfAssessmentFramework(adminId, competencyAssessmentId, model.FrameworkId.Value);
+ return RedirectToAction("SelectFrameworkSources", new { competencyAssessmentId, actionName = "Summary" });
+ }
+ else
+ {
+ competencyAssessmentService.UpdateFrameworkLinksTaskStatus(model.CompetencyAssessmentId, model.TaskStatus ?? false, null);
+ return RedirectToAction("ManageCompetencyAssessment", new { competencyAssessmentId = model.CompetencyAssessmentId });
+ }
+ }
+ [Route("/CompetencyAssessments/{competencyAssessmentId}/Frameworks/{frameworkId}/Remove")]
+ public IActionResult RemoveFramework(int frameworkId, int competencyAssessmentId)
+ {
+ var frameworkCompetencyCount = competencyAssessmentService.GetCompetencyCountByFrameworkId(competencyAssessmentId, frameworkId);
+ if (frameworkCompetencyCount > 0)
+ {
+ var adminId = GetAdminID();
+ var competencyAssessmentBase = competencyAssessmentService.GetCompetencyAssessmentBaseById(competencyAssessmentId, adminId);
+ var framework = frameworkService.GetFrameworkDetailByFrameworkId(frameworkId, adminId);
+ var model = new ConfirmRemoveFrameworkSourceViewModel(competencyAssessmentBase, framework, frameworkCompetencyCount);
+ return View("ConfirmRemoveFrameworkSource", model);
+ }
+ else
+ {
+ var adminId = GetAdminID();
+ competencyAssessmentService.RemoveSelfAssessmentFramework(competencyAssessmentId, frameworkId, adminId);
+ }
+ return RedirectToAction("SelectFrameworkSources", new { competencyAssessmentId, actionName = "Summary" });
+ }
+ [HttpPost]
+ [Route("/CompetencyAssessments/{competencyAssessmentId}/Frameworks/{frameworkId}/Remove")]
+ public IActionResult RemoveFramework(ConfirmRemoveFrameworkSourceViewModel model)
+ {
+ if (!ModelState.IsValid)
+ {
+ return View("ConfirmRemoveFrameworkSource", model);
+ }
+ var adminId = GetAdminID();
+ competencyAssessmentService.RemoveFrameworkCompetenciesFromAssessment(model.CompetencyAssessmentId, model.FrameworkId);
+ competencyAssessmentService.RemoveSelfAssessmentFramework(model.CompetencyAssessmentId, model.FrameworkId, adminId);
+ return RedirectToAction("SelectFrameworkSources", new { model.CompetencyAssessmentId, actionName = "Summary" });
+ }
+ [Route("/CompetencyAssessments/{competencyAssessmentId}/Competencies")]
+ public IActionResult ViewSelectedCompetencies(int competencyAssessmentId)
+ {
+
+ var competencies = competencyAssessmentService.GetCompetenciesForCompetencyAssessment(competencyAssessmentId);
+ var linkedFrameworks = competencyAssessmentService.GetLinkedFrameworksForCompetencyAssessment(competencyAssessmentId);
+ if (!competencies.Any())
+ {
+ return RedirectToAction("AddCompetenciesSelectFramework", new { competencyAssessmentId });
+ }
+ var adminId = GetAdminID();
+ var competencyAssessmentBase = competencyAssessmentService.GetCompetencyAssessmentBaseById(competencyAssessmentId, adminId);
+ var result = ValidateCompetencyAssessmentAndRole(competencyAssessmentId, adminId, "View Selected Competencies", competencyAssessmentBase);
+ if (result.StatusCode != 200)
+ return result;
+ var competencyAssessmentTaskStatus = competencyAssessmentService.GetCompetencyAssessmentTaskStatus(competencyAssessmentId, null);
+ var model = new ViewSelectedCompetenciesViewModel(competencyAssessmentBase, competencies, linkedFrameworks, competencyAssessmentTaskStatus.SelectCompetenciesTaskStatus);
+ return View(model);
+ }
+ [Route("/CompetencyAssessments/{competencyAssessmentId}/Competencies/Add/SelectFramework")]
+ public IActionResult AddCompetenciesSelectFramework(int competencyAssessmentId)
+ {
+ var linkedFrameworks = competencyAssessmentService.GetLinkedFrameworksForCompetencyAssessment(competencyAssessmentId);
+ if (!linkedFrameworks.Any())
+ {
+ return RedirectToAction("SelectFrameworkSources", new { competencyAssessmentId, actionName = "AddFramework" });
+ }
+ var adminId = GetAdminID();
+ var competencyAssessmentBase = competencyAssessmentService.GetCompetencyAssessmentBaseById(competencyAssessmentId, adminId);
+ var result = ValidateCompetencyAssessmentAndRole(competencyAssessmentId, adminId, "Select Framework Sources", competencyAssessmentBase);
+ if (result.StatusCode != 200)
+ return result;
+ var model = new AddCompetenciesSelectFrameworkViewModel(competencyAssessmentBase, linkedFrameworks);
+ return View(model);
+ }
+ [HttpPost]
+ [Route("/CompetencyAssessments/{competencyAssessmentId}/Competencies/Add/SelectFramework")]
+ public IActionResult AddCompetenciesSelectFramework(AddCompetenciesSelectFrameworkFormData formdata)
+ {
+ if (!ModelState.IsValid)
+ {
+ var competencyAssessmentId = formdata.ID;
+ var linkedFrameworks = competencyAssessmentService.GetLinkedFrameworksForCompetencyAssessment(competencyAssessmentId);
+ var adminId = GetAdminID();
+ var competencyAssessmentBase = competencyAssessmentService.GetCompetencyAssessmentBaseById(competencyAssessmentId, adminId);
+ var model = new AddCompetenciesSelectFrameworkViewModel(competencyAssessmentBase, linkedFrameworks);
+ model.FrameworkId = formdata.FrameworkId;
+ return View("AddCompetenciesSelectFramework", model);
+ }
+ else
+ {
+ return RedirectToAction("AddCompetencies", new { competencyAssessmentId = formdata.ID, frameworkId = formdata.FrameworkId });
+ }
+ }
+ [Route("/CompetencyAssessments/{competencyAssessmentId}/Competencies/Add/{frameworkId}")]
+ public IActionResult AddCompetencies(int competencyAssessmentId, int frameworkId)
+ {
+ var adminId = GetAdminID();
+ var competencyAssessmentBase = competencyAssessmentService.GetCompetencyAssessmentBaseById(competencyAssessmentId, adminId);
+ var result = ValidateCompetencyAssessmentAndRole(competencyAssessmentId, adminId, "Add Competencies", competencyAssessmentBase);
+ if (result.StatusCode != 200)
+ return result;
+ var framework = frameworkService.GetBaseFrameworkByFrameworkId(frameworkId, adminId);
+ var selectedFrameworkCompetencies = competencyAssessmentService.GetLinkedFrameworkCompetencyIds(competencyAssessmentId, frameworkId);
+ var groupedCompetencies = frameworkService.GetFrameworkCompetencyGroups(frameworkId, competencyAssessmentId);
+ var ungroupedCompetencies = frameworkService.GetFrameworkCompetenciesUngrouped(frameworkId, competencyAssessmentId);
+ var competencyIds = ungroupedCompetencies.Select(c => c.CompetencyID).ToArray();
+ var competencyFlags = frameworkService.GetSelectedCompetencyFlagsByCompetecyIds(competencyIds);
+ foreach (var competency in ungroupedCompetencies)
+ competency.CompetencyFlags = competencyFlags.Where(f => f.CompetencyId == competency.CompetencyID);
+ foreach (var group in groupedCompetencies)
+ {
+ competencyIds = group.FrameworkCompetencies.Select(c => c.CompetencyID).ToArray();
+ competencyFlags = frameworkService.GetSelectedCompetencyFlagsByCompetecyIds(competencyIds);
+ foreach (var competency in group.FrameworkCompetencies)
+ competency.CompetencyFlags = competencyFlags.Where(f => f.CompetencyId == competency.CompetencyID);
+ }
+ var model = new AddCompetenciesViewModel(competencyAssessmentBase, groupedCompetencies, ungroupedCompetencies, frameworkId, framework.FrameworkName, selectedFrameworkCompetencies);
+ return View(model);
+ }
+ [HttpPost]
+ [Route("/CompetencyAssessments/{competencyAssessmentId}/Competencies/Add/{frameworkId}")]
+ public IActionResult AddComptencies(AddCompetenciesFormData model, int competencyAssessmentId, int frameworkId)
+ {
+ if (!ModelState.IsValid)
+ {
+ //reload model and view
+ }
+ if (model.SelectedCompetencyIds != null)
+ {
+ competencyAssessmentService.InsertCompetenciesIntoAssessmentFromFramework(model.SelectedCompetencyIds, frameworkId, competencyAssessmentId);
+ }
+ var competencyAssessmentTaskStatus = competencyAssessmentService.GetCompetencyAssessmentTaskStatus(competencyAssessmentId, null);
+ if (competencyAssessmentTaskStatus.SelectCompetenciesTaskStatus != true)
+ {
+ competencyAssessmentService.UpdateSelectCompetenciesTaskStatus(competencyAssessmentId, false, null);
+ }
+ return RedirectToAction("ViewSelectedCompetencies", new { competencyAssessmentId });
+ }
+ [Route("/CompetencyAssessments/{competencyAssessmentId}/Competencies/Delete/{competencyId}")]
+ public IActionResult DeleteCompetency(int competencyAssessmentId, int competencyId)
+ {
+ competencyAssessmentService.RemoveCompetencyFromAssessment(competencyAssessmentId, competencyId);
+ return RedirectToAction("ViewSelectedCompetencies", new { competencyAssessmentId });
+ }
+
+ [Route("/CompetencyAssessments/{competencyAssessmentId}/CompetencyGroup/Delete/{competencyGroupId}/{competencyCount}/Confirm")]
+ public IActionResult DeleteCompetencyGroupConfirm(int competencyAssessmentId, int competencyGroupId, int competencyCount)
+ {
+ var adminId = GetAdminID();
+ CompetencyAssessmentBase? competencyAssessmentBase = competencyAssessmentService.GetCompetencyAssessmentBaseById(competencyAssessmentId, adminId);
+ if (competencyAssessmentBase == null)
+ {
+ logger.LogWarning($"Failed to load DeleteCompetencyGroupConfirm page for competencyAssessmentId: {competencyAssessmentId} adminId: {adminId}");
+ return StatusCode(500);
+ }
+ if (competencyAssessmentBase.UserRole < 2)
+ {
+ return StatusCode(403);
+ }
+ var model = new CompetencyGroupDeleteViewModel(competencyAssessmentId, competencyGroupId, competencyCount, competencyAssessmentBase.Vocabulary);
+
+ return View("RemoveCompetencyGroupConfirm", model);
+ }
+
+ public IActionResult DeleteCompetencyGroup(int competencyAssessmentId, int competencyGroupId)
+ {
+ competencyAssessmentService.RemoveCompetencyGroupFromAssessment(competencyAssessmentId, competencyGroupId);
+ return RedirectToAction("ViewSelectedCompetencies", new { competencyAssessmentId });
+ }
+
+
+ public IActionResult MoveCompetencyInSelfAssessment(int competencyAssessmentId, int competencyId, string direction)
+ {
+ var adminId = GetAdminID();
+ var competencyAssessmentBase = competencyAssessmentService.GetCompetencyAssessmentBaseById(competencyAssessmentId, adminId);
+ var result = ValidateCompetencyAssessmentAndRole(competencyAssessmentId, adminId, "Move Competency In Self Assessment", competencyAssessmentBase);
+ if (result.StatusCode != 200)
+ return result;
+ competencyAssessmentService.MoveCompetencyInSelfAssessment(competencyAssessmentId, competencyId, direction);
+ return new RedirectResult(Url.Action("ViewSelectedCompetencies", new { competencyAssessmentId }) + "#competency-" + competencyId.ToString());
+ }
+ public IActionResult MoveCompetencyGroupInSelfAssessment(int competencyAssessmentId, int groupId, string direction)
+ {
+ var adminId = GetAdminID();
+ var competencyAssessmentBase = competencyAssessmentService.GetCompetencyAssessmentBaseById(competencyAssessmentId, adminId);
+ var result = ValidateCompetencyAssessmentAndRole(competencyAssessmentId, adminId, "Move Competency Group In Self Assessment", competencyAssessmentBase);
+ if (result.StatusCode != 200)
+ return result;
+ competencyAssessmentService.MoveCompetencyGroupInSelfAssessment(competencyAssessmentId, groupId, direction);
+ return new RedirectResult(Url.Action("ViewSelectedCompetencies", new { competencyAssessmentId }) + "#group-" + groupId.ToString());
+ }
+ [HttpPost]
+ [Route("/CompetencyAssessments/{competencyAssessmentId}/Competencies")]
+ public IActionResult ViewSelectedCompetencies(ViewSelectedCompetenciesFormData model)
+ {
+ if (model.TaskStatus == null)
+ {
+ model.TaskStatus = false;
+ }
+ competencyAssessmentService.UpdateSelectCompetenciesTaskStatus(model.ID, model.TaskStatus.Value, null);
+ return RedirectToAction("ManageCompetencyAssessment", new { competencyAssessmentId = model.ID });
+ }
+ [HttpGet]
+ [Route("/CompetencyAssessments/{competencyAssessmentId}/Competencies/Optional/Manage")]
+ public IActionResult ManageOptionalCompetencies(int competencyAssessmentId)
+ {
+ var adminId = GetAdminID();
+ var competencyAssessmentBase = competencyAssessmentService.GetCompetencyAssessmentBaseById(competencyAssessmentId, adminId);
+ var result = ValidateCompetencyAssessmentAndRole(competencyAssessmentId, adminId, "Manage Optional Competencies", competencyAssessmentBase);
+ if (result.StatusCode != 200)
+ return result;
+ var competencies = competencyAssessmentService.GetCompetenciesForCompetencyAssessment(competencyAssessmentId).Where(c => c.Optional == true);
+ var competencyIds = competencies.Select(c => c.CompetencyID).ToArray();
+ var competencyFlags = frameworkService.GetSelectedCompetencyFlagsByCompetecyIds(competencyIds);
+ foreach (var competency in competencies)
+ competency.CompetencyFlags = competencyFlags.Where(f => f.CompetencyId == competency.CompetencyID);
+ var competencyAssessmentTaskStatus = competencyAssessmentService.GetCompetencyAssessmentTaskStatus(competencyAssessmentId, null);
+ var model = new SelectOptionalCompetenciesViewModel(competencyAssessmentBase, competencies, competencyAssessmentTaskStatus.OptionalCompetenciesTaskStatus);
+ return View(model);
+ }
+ [HttpPost]
+ [Route("/CompetencyAssessments/{competencyAssessmentId}/Competencies/Optional/Manage")]
+ public IActionResult ManageOptionalCompetencies(ViewSelectedCompetenciesFormData model)
+ {
+ if (model.TaskStatus == null)
+ {
+ model.TaskStatus = false;
+ }
+ competencyAssessmentService.UpdateOptionalCompetenciesTaskStatus(model.ID, model.TaskStatus.Value, null);
+ return RedirectToAction("ManageCompetencyAssessment", new { competencyAssessmentId = model.ID });
+ }
+ [HttpGet]
+ [Route("/CompetencyAssessments/{competencyAssessmentId}/Competencies/Optional/Select")]
+ public IActionResult SelectOptionalCompetencies(int competencyAssessmentId)
+ {
+ var adminId = GetAdminID();
+ var competencyAssessmentBase = competencyAssessmentService.GetCompetencyAssessmentBaseById(competencyAssessmentId, adminId);
+ var result = ValidateCompetencyAssessmentAndRole(competencyAssessmentId, adminId, "Select Optional Competencies", competencyAssessmentBase);
+ if (result.StatusCode != 200)
+ return result;
+ var competencies = competencyAssessmentService.GetCompetenciesForCompetencyAssessment(competencyAssessmentId);
+ var competencyIds = competencies.Select(c => c.CompetencyID).ToArray();
+ var competencyFlags = frameworkService.GetSelectedCompetencyFlagsByCompetecyIds(competencyIds);
+ foreach (var competency in competencies)
+ competency.CompetencyFlags = competencyFlags.Where(f => f.CompetencyId == competency.CompetencyID);
+ var competencyAssessmentTaskStatus = competencyAssessmentService.GetCompetencyAssessmentTaskStatus(competencyAssessmentId, null);
+ var model = new SelectOptionalCompetenciesViewModel(competencyAssessmentBase, competencies, competencyAssessmentTaskStatus.OptionalCompetenciesTaskStatus);
+ return View(model);
+ }
+ [HttpPost]
+ [Route("/CompetencyAssessments/{competencyAssessmentId}/Competencies/Optional/Select")]
+ public IActionResult SelectOptionalCompetencies(SelectOptionalCompetenciesFormData model)
+ {
+ if (!ModelState.IsValid)
+ {
+ var adminId = GetAdminID();
+ var competencyAssessmentBase = competencyAssessmentService.GetCompetencyAssessmentBaseById(model.ID, adminId);
+ var competencies = competencyAssessmentService.GetCompetenciesForCompetencyAssessment(model.ID);
+ var competencyIds = competencies.Select(c => c.CompetencyID).ToArray();
+ var competencyFlags = frameworkService.GetSelectedCompetencyFlagsByCompetecyIds(competencyIds);
+ foreach (var competency in competencies)
+ competency.CompetencyFlags = competencyFlags.Where(f => f.CompetencyId == competency.CompetencyID);
+ var viewModel = new SelectOptionalCompetenciesViewModel(competencyAssessmentBase, competencies, model.TaskStatus);
+ return View("SelectOptionalCompetencies", viewModel);
+ }
+ competencyAssessmentService.UpdateOptionalCompetenciesInAssessment(model.ID, model.GroupIds ?? [], model.SelectedCompetencyIds ?? []);
+ return RedirectToAction("ManageOptionalCompetencies", new { competencyAssessmentId = model.ID });
+ }
+ [HttpGet]
+ [Route("/CompetencyAssessments/{competencyAssessmentId}/Competencies/Optional/SetMinimum")]
+ public IActionResult SetMinimumOptionalCompetencies(int competencyAssessmentId)
+ {
+ var adminId = GetAdminID();
+ var competencyAssessmentBase = competencyAssessmentService.GetCompetencyAssessmentBaseById(competencyAssessmentId, adminId);
+ var result = ValidateCompetencyAssessmentAndRole(competencyAssessmentId, adminId, "Set Minimum Optional Competencies", competencyAssessmentBase);
+ if (result.StatusCode != 200)
+ return result;
+ var competencies = competencyAssessmentService.GetCompetenciesForCompetencyAssessment(competencyAssessmentId);
+ var viewModel = new SetMinimumOptionalCompetenciesViewModel(competencyAssessmentBase, competencies);
+ return View("SetMinimumOptionalCompetencies", viewModel);
+ }
+ [HttpPost]
+ [Route("/CompetencyAssessments/{competencyAssessmentId}/Competencies/Optional/SetMinimum")]
+ public IActionResult SetMinimumOptionalCompetencies(SetMinimumOptionalCompetenciesFormData model)
+ {
+ if (!ModelState.IsValid)
+ {
+ var adminId = GetAdminID();
+ var competencyAssessmentBase = competencyAssessmentService.GetCompetencyAssessmentBaseById(model.ID, adminId);
+ var competencies = competencyAssessmentService.GetCompetenciesForCompetencyAssessment(model.ID);
+ var viewModel = new SetMinimumOptionalCompetenciesViewModel(competencyAssessmentBase, competencies);
+ return View("SetMinimumOptionalCompetencies", viewModel);
+ }
+ competencyAssessmentService.UpdateMinimumOptionalCompetencies(model.ID, model.MinimumOptionalCompetencies ?? 0);
+ return RedirectToAction("ManageOptionalCompetencies", new { competencyAssessmentId = model.ID });
+ }
+ [HttpGet]
+ [Route("/CompetencyAssessments/{competencyAssessmentId}/Competencies/Optional/LearnerPrompt")]
+ public IActionResult SetOptionalCompetencyLearnerPrompt(int competencyAssessmentId)
+ {
+ var adminId = GetAdminID();
+ var competencyAssessmentBase = competencyAssessmentService.GetCompetencyAssessmentBaseById(competencyAssessmentId, adminId);
+ var result = ValidateCompetencyAssessmentAndRole(competencyAssessmentId, adminId, "Set Optional Competency Learner Prompt", competencyAssessmentBase);
+ if (result.StatusCode != 200)
+ return result;
+ var competencies = competencyAssessmentService.GetCompetenciesForCompetencyAssessment(competencyAssessmentId);
+ var viewModel = new SetOptionalCompetencyLearnerPromptViewModel(competencyAssessmentBase, competencies);
+ return View("SetOptionalCompetencyLearnerPrompt", viewModel);
+ }
+ [HttpPost]
+ [Route("/CompetencyAssessments/{competencyAssessmentId}/Competencies/Optional/LearnerPrompt")]
+ public IActionResult SetOptionalCompetencyLearnerPrompt(SetOptionalCompetencyLearnerPromptFormData model)
+ {
+ if (!ModelState.IsValid)
+ {
+ var adminId = GetAdminID();
+ var competencyAssessmentBase = competencyAssessmentService.GetCompetencyAssessmentBaseById(model.ID, adminId);
+ var competencies = competencyAssessmentService.GetCompetenciesForCompetencyAssessment(model.ID);
+ var viewModel = new SetOptionalCompetencyLearnerPromptViewModel(competencyAssessmentBase, competencies);
+ return View("SetOptionalCompetencyLearnerPrompt", viewModel);
+ }
+ competencyAssessmentService.UpdateManageOptionalCompetenciesPrompt(model.ID, model.ManageOptionalCompetenciesPrompt);
+ return RedirectToAction("ManageOptionalCompetencies", new { competencyAssessmentId = model.ID });
+ }
+
+ [Route("/CompetencyAssessments/Framework/{frameworkId}/{competencyAssessmentId}/Features")]
+ public IActionResult CompetencyAssessmentFeatures(int competencyAssessmentId, int? frameworkId = null)
+ {
+ var adminId = GetAdminID();
+ var data = GetcompetencyAssessmentFeaturesData();
+ if (!string.IsNullOrEmpty(data.CompetencyAssessmentName)) return View(data);
+ var competencyAssessmentBase = competencyAssessmentService.GetCompetencyAssessmentBaseById(competencyAssessmentId, adminId);
+ var result = ValidateCompetencyAssessmentAndRole(competencyAssessmentId, adminId, "Competency Assessment Features", competencyAssessmentBase);
+ if (result.StatusCode != 200)
+ return result;
+ var baseModel = new CompetencyAssessmentFeaturesViewModel(competencyAssessmentBase.ID,
+ competencyAssessmentBase.CompetencyAssessmentName,
+ competencyAssessmentBase.UserRole,
+ frameworkId);
+ return View(baseModel);
+ }
+ [HttpPost]
+ [Route("/CompetencyAssessments/Framework/{frameworkId}/{competencyAssessmentId}/Features")]
+ public IActionResult CompetencyAssessmentFeatures(CompetencyAssessmentFeaturesViewModel featuresViewModel)
+ {
+ if (featuresViewModel == null) return RedirectToAction("StatusCode", "LearningSolutions", new { code = 500 });
+ SetcompetencyAssessmentFeaturesData(featuresViewModel);
+ return RedirectToAction("CompetencyAssessmentSummary", new { competencyAssessmentId = featuresViewModel.ID, featuresViewModel.FrameworkId });
+ }
+
+ [Route("/CompetencyAssessments/Framework/{frameworkId}/{competencyAssessmentId}/Summary")]
+ public IActionResult CompetencyAssessmentSummary(int competencyAssessmentId, int? frameworkId = null)
+ {
+ if (competencyAssessmentService.GetSelfAssessmentStructure(competencyAssessmentId) != 0) return RedirectToAction("StatusCode", "LearningSolutions", new { code = 410 });
+ if (competencyAssessmentId == 0) return RedirectToAction("StatusCode", "LearningSolutions", new { code = 403 });
+ var data = GetcompetencyAssessmentFeaturesData();
+ if (data == null) return RedirectToAction("StatusCode", "LearningSolutions", new { code = 500 });
+ SetcompetencyAssessmentFeaturesData(data);
+ return View(data);
+ }
+ [HttpPost]
+ [Route("/CompetencyAssessments/Framework/{frameworkId}/{competencyAssessmentId}/Summary")]
+ public IActionResult CompetencyAssessmentSummary(CompetencyAssessmentFeaturesViewModel competency)
+ {
+ var data = GetcompetencyAssessmentFeaturesData();
+ if (competencyAssessmentService.GetSelfAssessmentStructure(data.ID) != 0) return RedirectToAction("StatusCode", "LearningSolutions", new { code = 410 });
+ if (data.ID == 0) return RedirectToAction("StatusCode", "LearningSolutions", new { code = 403 });
+ var features = competencyAssessmentService.UpdateCompetencyAssessmentFeaturesTaskStatus(data.ID,
+ data.DescriptionStatus,
+ data.ProviderandCategoryStatus,
+ data.VocabularyStatus,
+ data.WorkingGroupStatus,
+ data.AllframeworkCompetenciesStatus);
+ if (!features) return RedirectToAction("StatusCode", "LearningSolutions", new { code = 500 });
+ competencyAssessmentService.UpdateSelfAssessmentFromFramework(data.ID, data.FrameworkId);
+ competencyAssessmentService.InsertIntoSelfAssessmentCollaboratorsFromFrameworkCollaborators(data.ID, data.FrameworkId);
+ var insertSelfAssessmentGroupedCompetencies = competencyAssessmentService.InsertSelfAssessmentGroupedCompetencies(data.ID, data.FrameworkId);
+ var insertSelfAssessmentUngroupedCompetencies = competencyAssessmentService.InsertSelfAssessmentUngroupedCompetencies(data.ID, data.FrameworkId);
+ if (!insertSelfAssessmentGroupedCompetencies) return RedirectToAction("StatusCode", "LearningSolutions", new { code = 500 });
+ multiPageFormService.ClearMultiPageFormData(MultiPageFormDataFeature.AddCustomWebForm("AssessmentFeaturesDataCWF"), TempData);
+ TempData.Clear();
+ return RedirectToAction("ManageCompetencyAssessment", new { competencyAssessmentId = competency.ID, competency.FrameworkId });
+ }
+
+ [Route("/CompetencyAssessments/{competencyAssessmentId}/Frameworks/{frameworkId}/Make")]
+ public IActionResult ConfirmMaKePrimaryFramework(int frameworkId, int competencyAssessmentId)
+ {
+ var adminId = GetAdminID();
+ var competencyAssessmentBase = competencyAssessmentService.GetCompetencyAssessmentBaseById(competencyAssessmentId, adminId);
+ var result = ValidateCompetencyAssessmentAndRole(competencyAssessmentId, adminId, "Confirm MaKe Primary Framework", competencyAssessmentBase);
+ if (result.StatusCode != 200)
+ return result;
+ var framework = frameworkService.GetFrameworkDetailByFrameworkId(frameworkId, adminId);
+ var model = new ConfirmMakePrimaryFrameworkViewModel(competencyAssessmentBase, framework);
+ return View("ConfirmMaKePrimaryFramework", model);
+ }
+ [HttpPost]
+ [Route("/CompetencyAssessments/{competencyAssessmentId}/Frameworks/{frameworkId}/Make")]
+ public IActionResult ConfirmMaKePrimaryFramework(ConfirmMakePrimaryFrameworkViewModel model)
+ {
+ if (!ModelState.IsValid)
+ {
+ return View("ConfirmMaKePrimaryFramework", model);
+ }
+ competencyAssessmentService.UpdatePrimaryFrameworkCompetencies(model.CompetencyAssessmentId, model.FrameworkId);
+ var features = competencyAssessmentService.UpdateCompetencyAssessmentFeaturesTaskStatus(model.CompetencyAssessmentId,
+ model.DescriptionStatus,
+ model.ProviderandCategoryStatus,
+ model.VocabularyStatus,
+ model.WorkingGroupStatus,
+ model.AllframeworkCompetenciesStatus);
+ competencyAssessmentService.InsertIntoSelfAssessmentCollaboratorsFromFrameworkCollaborators(model.CompetencyAssessmentId, model.FrameworkId);
+ competencyAssessmentService.UpdateSelfAssessmentFromFramework(model.CompetencyAssessmentId, model.FrameworkId);
+ return RedirectToAction("ManageCompetencyAssessment", new { model.CompetencyAssessmentId, model.FrameworkId });
+ }
+
+
+ [Route("/CompetencyAssessments/{competencyAssessmentId}/{actionName}")]
+ public IActionResult AssessmentWorkingGroup(int competencyAssessmentId, string actionName)
+ {
+ var adminId = GetAdminID();
+
+ var collaborators = competencyAssessmentService.GetCollaboratorsForCompetencyAssessmentId(competencyAssessmentId);
+ var competencyAssessmentBase = competencyAssessmentService.GetCompetencyAssessmentBaseById(competencyAssessmentId, adminId);
+ var result = ValidateCompetencyAssessmentAndRole(competencyAssessmentId, adminId, "Assessment Working Group", competencyAssessmentBase);
+ if (result.StatusCode != 200)
+ return result;
+ var taskStatus = competencyAssessmentService.GetCompetencyAssessmentTaskStatus(competencyAssessmentId, null);
+ var model = new WorkingGroupCollaboratorsViewModel()
+ {
+ CompetencyAssessmentID = competencyAssessmentId,
+ Collaborators = collaborators,
+ CompetencyAssessmentTaskStatus = taskStatus.WorkingGroupTaskStatus,
+ UserEmail = null,
+ Error = false,
+ ActionName = actionName
+ };
+ if (TempData["CompetencyAssessmentError"] != null)
+ {
+ ModelState.AddModelError("userEmail", TempData.Peek("CompetencyAssessmentError").ToString());
+ }
+ return View("CompetencyAssessmentWorkingGroup", model);
+ }
+
+ [HttpPost]
+ [Route("/CompetencyAssessments/{competencyAssessmentId}/{actionName}")]
+ public IActionResult AssessmentWorkingGroup(WorkingGroupCollaboratorsViewModel model, bool canModify, string actionName)
+ {
+ int? centreID = GetCentreId();
+ if (actionName == "Collaborators" || actionName == "CollaboratorReview")
+ {
+ var collaboratorId = competencyAssessmentService.AddCollaboratorToCompetencyAssessment(model.CompetencyAssessmentID, model.UserEmail, canModify, centreID);
+ if (collaboratorId > 0)
+ {
+ selfAssessmentNotificationService.SendCompetencyAssessmentCollaboratorInvite(collaboratorId, GetAdminID());
+ }
+ else
+ {
+ if (collaboratorId == -3)
+ {
+ TempData["CompetencyAssessmentError"] = "Email address should not be empty";
+
+ }
+ else if (collaboratorId == -2)
+ {
+ TempData["CompetencyAssessmentError"] = $"A user with the email address has been previously added";
+ }
+ else if (collaboratorId == -4)
+ {
+ TempData["CompetencyAssessmentError"] = $"The email address must match a registered DLS Admin account";
+ }
+ else if (collaboratorId == -5)
+ {
+ TempData["CompetencyAssessmentError"] = $"The owner cannot be the collaborator of the competency assessment.";
+ }
+ else
+ {
+ TempData["CompetencyAssessmentError"] = "User not added,Kindly try again;";
+ }
+ }
+ return RedirectToAction("AssessmentWorkingGroup", "CompetencyAssessments", new { model.CompetencyAssessmentID, actionName = actionName });
+
+ }
+ else
+ {
+ competencyAssessmentService.UpdateWorkingGroupTaskStatus(model.CompetencyAssessmentID, model.CompetencyAssessmentTaskStatus ?? false, null);
+ return RedirectToAction("ManageCompetencyAssessment", "CompetencyAssessments", new { model.CompetencyAssessmentID });
+ }
+ }
+
+ [Route("/CompetencyAssessments/{competencyAssessmentId}/ConfigureOptions")]
+ public IActionResult ConfigureOptions(int competencyAssessmentId)
+ {
+ var data = new OptionsLabelsViewModel();
+ var adminId = GetAdminID();
+
+ var competencyAssessmentBase = competencyAssessmentService.GetCompetencyAssessmentBaseById(competencyAssessmentId, adminId);
+ var result = ValidateCompetencyAssessmentAndRole(competencyAssessmentId, adminId, "competency assessment options", competencyAssessmentBase);
+ if (result.StatusCode != 200)
+ return result;
+ data.CompetencyAssessmentID = competencyAssessmentId;
+ data.Vocabulary = competencyAssessmentBase.Vocabulary;
+ data.IncludeLearnerDeclarationPrompt = competencyAssessmentBase.IncludeLearnerDeclarationPrompt;
+ data.IncludesSignposting = competencyAssessmentBase.IncludesSignposting;
+ data.LinearNavigation = competencyAssessmentBase.LinearNavigation;
+ data.UseDescriptionExpanders = competencyAssessmentBase.UseDescriptionExpanders;
+ data.QuestionLabel = string.IsNullOrWhiteSpace(competencyAssessmentBase.QuestionLabel) ? false : true;
+ data.QuestionLabelText = competencyAssessmentBase.QuestionLabel?.Trim();
+ data.ReviewerCommentsLabel = string.IsNullOrWhiteSpace(competencyAssessmentBase.ReviewerCommentsLabel) ? false : true;
+ data.ReviewerCommentsLabelText = competencyAssessmentBase.ReviewerCommentsLabel?.Trim();
+ data.IsSupervisionSwitchedOn = competencyAssessmentBase.SupervisorSelfAssessmentReview && competencyAssessmentBase.SupervisorResultsReview;
+ data.IsSignpostedLearning = competencyAssessmentService.HasCompetencyWithSignpostedLearning(competencyAssessmentId);
+
+ var taskStatus = competencyAssessmentService.GetCompetencyAssessmentTaskStatus(competencyAssessmentId, null);
+ data.SelfAssessmentOptionsTaskStatus = taskStatus.SelfAssessmentOptionsTaskStatus;
+
+ SetOptionsLabelsData(data);
+
+ var step = (int)OptionLabel.Declaration;
+ if (taskStatus.SelfAssessmentOptionsTaskStatus != null)
+ step = (int)OptionLabel.Summary;
+
+ ValidateStep(data, ref step);
+ return RedirectToAction("OptionsLabels", "CompetencyAssessments", new { competencyAssessmentId, step });
+ }
+
+ [Route("/CompetencyAssessments/{competencyAssessmentId}/OptionsLabels/{step}")]
+ public IActionResult OptionsLabels(int competencyAssessmentId, int step)
+ {
+ if (step < (int)OptionLabel.Declaration || step > (int)OptionLabel.Summary)
+ return StatusCode(500);
+
+ var adminId = GetAdminID();
+ var result = ValidateCompetencyAssessmentAndRole(competencyAssessmentId, adminId, "competency assessment options");
+ if (result.StatusCode != 200)
+ return result;
+
+ var data = GetOptionsLabelslData();
+
+ if (ValidateStep(data, ref step))
+ {
+ return RedirectToAction("OptionsLabels", "CompetencyAssessments", new { competencyAssessmentId, step });
+ }
+
+ data.CurrentStep = step;
+ var model = new OptionsLabelsViewModel(data);
+
+ return View("CompetencyAssessmentOptions", model);
+ }
+
+ [HttpPost]
+ [Route("/CompetencyAssessments/{competencyAssessmentId}/OptionsLabels/{step}")]
+ public IActionResult OptionsLabels(OptionsLabelsViewModel model)
+ {
+ var adminId = GetAdminID();
+ var data = GetOptionsLabelslData();
+ data.CurrentStep = model.CurrentStep;
+ ModelState.Remove("VocabularySingular");
+ ModelState.Remove("VocabularyPlural");
+
+ if (model.CurrentStep == (int)OptionLabel.Declaration)
+ {
+ data.IncludeLearnerDeclarationPrompt = model.IncludeLearnerDeclarationPrompt;
+ }
+ else if (model.CurrentStep == (int)OptionLabel.Signposting)
+ {
+ data.IncludesSignposting = model.IncludesSignposting;
+ }
+ else if (model.CurrentStep == (int)OptionLabel.LinearNavigation)
+ {
+ data.LinearNavigation = model.LinearNavigation;
+ }
+ else if (model.CurrentStep == (int)OptionLabel.DescriptionExpanders)
+ {
+ data.UseDescriptionExpanders = model.UseDescriptionExpanders;
+ }
+ else if (model.CurrentStep == (int)OptionLabel.QuestionLabels)
+ {
+ if (model.QuestionLabel)
+ {
+ var label = model.QuestionLabelText?.Trim();
+ if (string.IsNullOrEmpty(label))
+ ModelState.AddModelError(nameof(model.QuestionLabelText), "Please enter a question label");
+ else if (label.Length > 50)
+ ModelState.AddModelError(nameof(model.QuestionLabelText), "Question label must be 50 characters or fewer");
+
+ if (!ModelState.IsValid)
+ {
+ var errModel = new OptionsLabelsViewModel(data);
+ errModel.QuestionLabel = model.QuestionLabel;
+ errModel.QuestionLabelText = model.QuestionLabelText;
+ errModel.Error = true;
+ return View("CompetencyAssessmentOptions", errModel);
+ }
+ }
+ data.QuestionLabel = model.QuestionLabel;
+ data.QuestionLabelText = model.QuestionLabel ? model.QuestionLabelText.Trim() : null;
+ }
+ else if (model.CurrentStep == (int)OptionLabel.CommentsLabel)
+ {
+ if (model.ReviewerCommentsLabel)
+ {
+ var label = model.ReviewerCommentsLabelText?.Trim();
+ if (string.IsNullOrEmpty(label))
+ ModelState.AddModelError(nameof(model.ReviewerCommentsLabelText), "Please enter a reviewer comment");
+ else if (label.Length > 50)
+ ModelState.AddModelError(nameof(model.ReviewerCommentsLabelText), "Reviewer comment must be 50 characters or fewer");
+
+ if (!ModelState.IsValid)
+ {
+ var errModel = new OptionsLabelsViewModel(data);
+ errModel.ReviewerCommentsLabel = model.ReviewerCommentsLabel;
+ errModel.ReviewerCommentsLabelText = model.ReviewerCommentsLabelText;
+ errModel.Error = true;
+ return View("CompetencyAssessmentOptions", errModel);
+ }
+ }
+ data.ReviewerCommentsLabel = model.ReviewerCommentsLabel;
+ data.ReviewerCommentsLabelText = model.ReviewerCommentsLabel ? model.ReviewerCommentsLabelText.Trim() : null;
+ }
+ else if (model.CurrentStep == (int)OptionLabel.Summary)
+ {
+ var isUpdate = competencyAssessmentService.UpdateCompetencyAssessmentOptions(
+ data.IncludeLearnerDeclarationPrompt,
+ data.IncludesSignposting,
+ data.LinearNavigation,
+ data.UseDescriptionExpanders,
+ data.QuestionLabel ? data.QuestionLabelText?.Trim() : null,
+ data.ReviewerCommentsLabel ? data.ReviewerCommentsLabelText?.Trim() : null,
+ data.CompetencyAssessmentID,
+ adminId);
+ if (!isUpdate)
+ {
+ ModelState.AddModelError("", "Update failed. Please try again.");
+ return View("CompetencyAssessmentOptions", model);
+ }
+ competencyAssessmentService.UpdateCompetencyAssessmentOptionsTaskStatus(model.CompetencyAssessmentID, model.SelfAssessmentOptionsTaskStatus ?? false);
+ return RedirectToAction("ManageCompetencyAssessment", "CompetencyAssessments", new { model.CompetencyAssessmentID });
+ }
+
+ if (data.SelfAssessmentOptionsTaskStatus != null)
+ data.CurrentStep = (int)OptionLabel.Summary;
+ else
+ data.CurrentStep = model.CurrentStep + 1;
+
+ SetOptionsLabelsData(data);
+
+ var newModel = new OptionsLabelsViewModel(data);
+ return RedirectToAction("OptionsLabels", "CompetencyAssessments", new { model.CompetencyAssessmentID, step = newModel.CurrentStep });
+ }
+
+ public IActionResult RemoveCollaborator(int competencyAssessmentId, int id, string actionName)
+ {
+ competencyAssessmentService.RemoveCollaboratorFromCompetencyAssessment(competencyAssessmentId, id);
+ return RedirectToAction("AssessmentWorkingGroup", "CompetencyAssessments", new { competencyAssessmentId, actionName = actionName });
+ }
+
+ [HttpGet]
+ [Route("/CompetencyAssessments/{competencyAssessmentId}/RoleRequirements/")]
+ public IActionResult ManageCompetencyRoleRequirements(int competencyAssessmentId)
+ {
+ var adminId = GetAdminID();
+ var competencyAssessmentBase = competencyAssessmentService.GetCompetencyAssessmentBaseById(competencyAssessmentId, adminId);
+ var result = ValidateCompetencyAssessmentAndRole(competencyAssessmentId, adminId, "Manage Competency Role Requirements", competencyAssessmentBase);
+ if (result.StatusCode != 200)
+ return result;
+ var groupedCompetencyWithAssessmentRoleRequirements = competencyAssessmentService.GetGroupedCompetencyWithAssessmentRoleRequirements(competencyAssessmentId, null, null);
+ var taskStatus = competencyAssessmentService.GetCompetencyAssessmentTaskStatus(competencyAssessmentId, null);
+ var model = new ManageCompetencyRoleRequirementsViewModel(competencyAssessmentBase, groupedCompetencyWithAssessmentRoleRequirements, taskStatus);
+ return View("ManageCompetencyRoleRequirements", model);
+ }
+ [HttpPost]
+ [Route("/CompetencyAssessments/{competencyAssessmentId}/RoleRequirements/")]
+ public IActionResult ManageCompetencyRoleRequirements(ManageCompetencyRoleRequirementsFormData model)
+ {
+ var adminId = GetAdminID();
+ var competencyAssessmentBase = competencyAssessmentService.GetCompetencyAssessmentBaseById(model.Id, adminId);
+ var result = ValidateCompetencyAssessmentAndRole(model.Id, adminId, "Manage Competency Role Requirements", competencyAssessmentBase);
+ if (result.StatusCode != 200)
+ return result;
+ competencyAssessmentService.UpdateCompetencyAssessmentRoleRequirementsTaskStatus(model.Id, model.TaskStatus ?? false, null);
+ return RedirectToAction("ManageCompetencyAssessment", new { competencyAssessmentId = model.Id });
+ }
+ [HttpGet]
+ [Route("/CompetencyAssessments/{competencyAssessmentId}/RoleRequirements/Enforce")]
+ public IActionResult EditEnforceRoleRequirementsForSignOff(int competencyAssessmentId)
+ {
+ var adminId = GetAdminID();
+ var competencyAssessmentBase = competencyAssessmentService.GetCompetencyAssessmentBaseById(competencyAssessmentId, adminId);
+ var result = ValidateCompetencyAssessmentAndRole(competencyAssessmentId, adminId, "Set Enforce Role Requirements For Sign Off", competencyAssessmentBase);
+ if (result.StatusCode != 200)
+ return result;
+ var model = new EditRoleRequirementsFlagsViewModel(competencyAssessmentBase);
+ return View("EnforceRoleRequirementsForSignOff", model);
+ }
+ [HttpGet]
+ [Route("/CompetencyAssessments/{competencyAssessmentId}/RoleRequirements/IncludeFilters")]
+ public IActionResult EditIncludeRequirementsFilters(int competencyAssessmentId)
+ {
+ var adminId = GetAdminID();
+ var competencyAssessmentBase = competencyAssessmentService.GetCompetencyAssessmentBaseById(competencyAssessmentId, adminId);
+ var result = ValidateCompetencyAssessmentAndRole(competencyAssessmentId, adminId, "Set Enforce Role Requirements For Sign Off", competencyAssessmentBase);
+ if (result.StatusCode != 200)
+ return result;
+ var model = new EditRoleRequirementsFlagsViewModel(competencyAssessmentBase);
+ return View("EditIncludeRequirementsFilters", model);
+ }
+ [HttpPost]
+ public IActionResult EditRoleRequirementsFlags(ManageCompetencyRoleRequirementsFormData model)
+ {
+ var adminId = GetAdminID();
+ var competencyAssessmentBase = competencyAssessmentService.GetCompetencyAssessmentBaseById(model.Id, adminId);
+ var result = ValidateCompetencyAssessmentAndRole(model.Id, adminId, "Set Enforce Role Requirements For Sign Off", competencyAssessmentBase);
+ if (result.StatusCode != 200)
+ return result;
+ competencyAssessmentService.UpdateRoleRequirementsFlags(model.Id, model.EnforceRoleRequirementsForSignOff, model.IncludeRequirementsFilters);
+ return RedirectToAction("ManageCompetencyRoleRequirements", new { competencyAssessmentId = model.Id });
+ }
+ [HttpGet]
+ [Route("/CompetencyAssessments/{competencyAssessmentId}/RoleRequirements/Edit")]
+ public IActionResult EditCompetencyRoleRequirements(int competencyAssessmentId)
+ {
+ var adminId = GetAdminID();
+ var competencyAssessmentBase = competencyAssessmentService.GetCompetencyAssessmentBaseById(competencyAssessmentId, adminId);
+ var result = ValidateCompetencyAssessmentAndRole(competencyAssessmentId, adminId, "Manage Competency Role Requirements", competencyAssessmentBase);
+ if (result.StatusCode != 200)
+ return result;
+ var groupedCompetencyWithAssessmentRoleRequirements = competencyAssessmentService.GetGroupedCompetencyWithAssessmentRoleRequirements(competencyAssessmentId, null, null);
+ var model = new EditCompetencyRoleRequirementsViewModel(competencyAssessmentBase, groupedCompetencyWithAssessmentRoleRequirements);
+ return View("EditCompetencyRoleRequirements", model);
+ }
+ [HttpGet]
+ [Route("/CompetencyAssessments/{competencyAssessmentId}/RoleRequirements/Edit/Competency/{competencyId}/Question/{assessmentQuestionId}")]
+ public IActionResult EditQuestionResponseRoleRequirements(int competencyAssessmentId, int competencyId, int assessmentQuestionId)
+ {
+ var adminId = GetAdminID();
+ var competencyAssessmentBase = competencyAssessmentService.GetCompetencyAssessmentBaseById(competencyAssessmentId, adminId);
+ var result = ValidateCompetencyAssessmentAndRole(competencyAssessmentId, adminId, "Edit Competency Role Requirement Question Cells", competencyAssessmentBase);
+ if (result.StatusCode != 200)
+ return result;
+ var assessmentQuestion = competencyAssessmentService.GetGroupedCompetencyWithAssessmentRoleRequirements(competencyAssessmentId, competencyId, assessmentQuestionId);
+ var countAssessmentQuestionInSelfAssessment = competencyAssessmentService.GetCountOfAsssessmentQuestionInCompetencyAssessment(competencyAssessmentId, assessmentQuestionId);
+ var model = new EditQuestionResponseRoleRequirementsViewModel(competencyAssessmentBase, assessmentQuestion, countAssessmentQuestionInSelfAssessment, competencyId, assessmentQuestionId);
+ return View("EditQuestionResponseRoleRequirements", model);
+ }
+ [HttpPost]
+ [Route("/CompetencyAssessments/{competencyAssessmentId}/RoleRequirements/Edit/Competency/{competencyId}/Question/{assessmentQuestionId}")]
+ public IActionResult EditQuestionResponseRoleRequirements(EditQuestionResponseRoleRequirementsFormData model)
+ {
+ var adminId = GetAdminID();
+ var competencyAssessmentBase = competencyAssessmentService.GetCompetencyAssessmentBaseById(model.Id, adminId);
+ var result = ValidateCompetencyAssessmentAndRole(model.Id, adminId, "Edit Competency Role Requirement Question Cells", competencyAssessmentBase);
+ if (result.StatusCode != 200)
+ return result;
+ if (model.ApplyToAll)
+ {
+ competencyAssessmentService.UpdateAssessmentQuestionRoleRequirementsForSelfAssessment(model.Id, model.AssessmentQuestionId, model.ResponseRoleRequirements);
+ }
+ else
+ {
+ competencyAssessmentService.UpdateCompetencyAssessmentQuestionRoleRequirement(model.Id, model.CompetencyId, model.AssessmentQuestionId, model.ResponseRoleRequirements);
+ }
+
+ return RedirectToAction("EditCompetencyRoleRequirements", new { competencyAssessmentId = model.Id });
+ }
+ [Route("/CompetencyAssessments/{competencyAssessmentId}/SupervisorRoles")]
+ public IActionResult SupervisorRoles(int competencyAssessmentId)
+ {
+ var adminId = GetAdminID();
+ var competencyAssessmentBase = competencyAssessmentService.GetCompetencyAssessmentBaseById(competencyAssessmentId, adminId);
+ var result = ValidateCompetencyAssessmentAndRole(competencyAssessmentId, adminId, "Supervisor Roles", competencyAssessmentBase);
+ if (result.StatusCode != 200)
+ return result;
+
+ var model = new ManagesupervisionViewModel(competencyAssessmentId, competencyAssessmentBase.CompetencyAssessmentName,
+ competencyAssessmentBase.SupervisorResultsReview,
+ competencyAssessmentBase.SupervisorSelfAssessmentReview,
+ competencyAssessmentBase.SignOffSupervisorStatement,
+ competencyAssessmentBase.SignOffRequestorStatement,
+ this.config.GetLearnerDefaultText(),
+ this.config.GetSupervisorDefaultText());
+
+ SetManagesupervisionData(model);
+
+ var competencyAssessmentTaskStatus = competencyAssessmentService.GetCompetencyAssessmentTaskStatus(competencyAssessmentId, null);
+ if (competencyAssessmentTaskStatus.SupervisorRolesTaskStatus != null)
+ return RedirectToAction("ManageSupervisionSettings", "CompetencyAssessments",
+ new
+ {
+ CompetencyAssessmentId = competencyAssessmentId,
+ ActionName = "SupervisorRoles"
+ });
+ return RedirectToAction("SupervisedSelfAssessmentSignoff", "CompetencyAssessments", new { competencyAssessmentId });
+ }
+
+ [Route("/CompetencyAssessments/{competencyAssessmentId}/Supervised")]
+ [Route("/CompetencyAssessments/{competencyAssessmentId}/{actionName}/Supervised")]
+ public IActionResult SupervisedSelfAssessmentSignoff(int competencyAssessmentId, string? actionName)
+ {
+ if (actionName == "Signoff")
+ {
+ var data = GetManagesupervisionData();
+ data.Signoff.ActionName = actionName;
+ var models = new SupervisedSelfAssessmentSignoffViewModel(data.Signoff);
+ return View(models);
+ }
+ var adminId = GetAdminID();
+ var baseData = competencyAssessmentService.GetCompetencyAssessmentBaseById(competencyAssessmentId, adminId);
+ var result = ValidateCompetencyAssessmentAndRole(competencyAssessmentId, adminId, "Supervised Self Assessment Signoff", baseData);
+ if (result.StatusCode != 200)
+ return result;
+ var model = new SupervisedSelfAssessmentSignoffViewModel(competencyAssessmentId, baseData.CompetencyAssessmentName, actionName);
+ return View("SupervisedSelfAssessmentSignoff", model);
+ }
+
+ [HttpPost]
+ [Route("/CompetencyAssessments/{competencyAssessmentId}/Supervised")]
+ public IActionResult SupervisedSelfAssessmentSignoff(SupervisedSelfAssessmentSignoffViewModel supervisedSelf)
+ {
+ if (supervisedSelf == null) return RedirectToAction("StatusCode", "LearningSolutions", new { code = 403 });
+ if (supervisedSelf.Signoff == null)
+ {
+ ModelState.AddModelError(nameof(supervisedSelf.Signoff), "Please select a Supervisor signs off self assessment option");
+ return View(supervisedSelf);
+ }
+ if (supervisedSelf.Confirm == null && supervisedSelf.SignoffText == "Yes")
+ {
+ ModelState.AddModelError(nameof(supervisedSelf.Confirm), "Please select a Supervisor or nominated supervisor confirm individual assessment option");
+ return View(supervisedSelf);
+ }
+ if (supervisedSelf.SignoffText == "No")
+ {
+ var model = new ManagesupervisionViewModel(supervisedSelf);
+ SetManagesupervisionData(model);
+ return RedirectToAction("ManageSupervisionSettings", "CompetencyAssessments", new { supervisedSelf.CompetencyAssessmentId });
+ }
+ else if (supervisedSelf.ActionName != null)
+ {
+ var data = GetManagesupervisionData();
+ data.CompetencyAssessmentId = supervisedSelf.CompetencyAssessmentId;
+ var model = new ManagesupervisionViewModel(data.LearnerDeclaration, data.SupervisorDeclaration, supervisedSelf);
+ model.CompetencyAssessmentId = supervisedSelf.CompetencyAssessmentId;
+ SetManagesupervisionData(model);
+ return RedirectToAction("SupervisorSignoffDeclaration", "CompetencyAssessments",
+ new
+ {
+ CompetencyAssessmentId = supervisedSelf.CompetencyAssessmentId,
+ ActionName = "Supervisor"
+ });
+ }
+ else
+ {
+ var model = new ManagesupervisionViewModel(supervisedSelf);
+ SetManagesupervisionData(model);
+ var competencyAssessmentTaskStatus = competencyAssessmentService.GetCompetencyAssessmentTaskStatus(supervisedSelf.CompetencyAssessmentId, null);
+ return RedirectToAction("SupervisorSignoffDeclaration", "CompetencyAssessments", new { supervisedSelf.CompetencyAssessmentId });
+ }
+ }
+
+ [Route("/CompetencyAssessments/{competencyAssessmentId}/SupervisorDeclaration")]
+ [Route("/CompetencyAssessments/{competencyAssessmentId}/{actionName}/SupervisorDeclaration")]
+ public IActionResult SupervisorSignoffDeclaration(int competencyAssessmentId, string? actionName)
+ {
+ var adminId = GetAdminID();
+ var baseData = competencyAssessmentService.GetCompetencyAssessmentBaseById(competencyAssessmentId, adminId);
+ var result = ValidateCompetencyAssessmentAndRole(competencyAssessmentId, adminId, "Supervisor Signoff Declaration", baseData);
+ if (result.StatusCode != 200)
+ return result;
+ var data = GetManagesupervisionData();
+ if (actionName == "Supervisor")
+ {
+ data.CompetencyAssessmentId = competencyAssessmentId;
+ data.SupervisorDeclaration.DefaultText = this.config.GetSupervisorDefaultText();
+ data.SupervisorDeclaration.ActionName = actionName;
+ var models = new SupervisorSignoffDeclarationViewModel(data.SupervisorDeclaration);
+ return View(models);
+ }
+ var model = new SupervisorSignoffDeclarationViewModel(competencyAssessmentId);
+ model.CompetencyAssessmentName = data.CompetencyAssessmentName;
+ model.CompetencyAssessmentId = competencyAssessmentId;
+ model.DefaultText = this.config.GetSupervisorDefaultText().Replace("{{CompetencyAssessmentName}}", model.CompetencyAssessmentName); ;
+ return View(model);
+ }
+
+ [HttpPost]
+ [Route("/CompetencyAssessments/{competencyAssessmentId}/SupervisorDeclaration")]
+ public IActionResult SupervisorSignoffDeclaration(SupervisorSignoffDeclarationViewModel viewModel)
+ {
+ if (viewModel == null) return RedirectToAction("StatusCode", "LearningSolutions", new { code = 403 });
+ if (viewModel.DeclarationValue == null)
+ {
+ ModelState.AddModelError(nameof(viewModel.DeclarationValue), "Please select a declaration option");
+ return View(viewModel);
+ }
+ else if (viewModel.DeclarationValue == 1 && string.IsNullOrWhiteSpace(viewModel.CustomText))
+ {
+ ModelState.AddModelError(nameof(viewModel.CustomText), "Please enter the custom declaration text");
+ return View(viewModel);
+ }
+ else if (viewModel.DeclarationValue == 1 && viewModel.CustomText.Length > 2000)
+ {
+ ModelState.AddModelError(nameof(viewModel.CustomText), "Declaration text must be 2000 characters or fewer");
+ return View(viewModel);
+ }
+
+ var data = GetManagesupervisionData();
+ var model = new ManagesupervisionViewModel(data.LearnerDeclaration, viewModel, data.Signoff);
+ model.CompetencyAssessmentName = viewModel.CompetencyAssessmentName;
+ SetManagesupervisionData(model);
+ if (viewModel.ActionName != null) return RedirectToAction("LearnerSignoffDeclaration", "CompetencyAssessments",
+ new
+ {
+ CompetencyAssessmentId = viewModel.CompetencyAssessmentId,
+ ActionName = "Learner"
+ });
+ return RedirectToAction("LearnerSignoffDeclaration", "CompetencyAssessments", new { viewModel.CompetencyAssessmentId });
+ }
+
+ [Route("/CompetencyAssessments/{competencyAssessmentId}/LearnerDeclaration")]
+ [Route("/CompetencyAssessments/{competencyAssessmentId}/{actionName}/LearnerDeclaration")]
+ public IActionResult LearnerSignoffDeclaration(int competencyAssessmentId, string? actionName)
+ {
+ var adminId = GetAdminID();
+ var baseData = competencyAssessmentService.GetCompetencyAssessmentBaseById(competencyAssessmentId, adminId);
+ var result = ValidateCompetencyAssessmentAndRole(competencyAssessmentId, adminId, "Learner Signoff Declaration", baseData);
+ if (result.StatusCode != 200)
+ return result;
+ var data = GetManagesupervisionData();
+ if (actionName == "Learner")
+ {
+ data.CompetencyAssessmentId = competencyAssessmentId;
+ data.LearnerDeclaration.DefaultText = this.config.GetLearnerDefaultText();
+ data.LearnerDeclaration.ActionName = actionName;
+ var models = new LearnerSignoffDeclarationViewModel(data.LearnerDeclaration);
+ models.CompetencyAssessmentId = competencyAssessmentId;
+ return View(models);
+ }
+ var model = new LearnerSignoffDeclarationViewModel(competencyAssessmentId);
+ model.CompetencyAssessmentName = data.CompetencyAssessmentName;
+ model.CompetencyAssessmentId = competencyAssessmentId;
+ model.DefaultText = this.config.GetLearnerDefaultText().Replace("{{CompetencyAssessmentName}}", model.CompetencyAssessmentName);
+ return View(model);
+ }
+
+ [HttpPost]
+ [Route("/CompetencyAssessments/{competencyAssessmentId}/LearnerDeclaration")]
+ public IActionResult LearnerSignoffDeclaration(LearnerSignoffDeclarationViewModel viewModel)
+ {
+ if (viewModel == null) return RedirectToAction("StatusCode", "LearningSolutions", new { code = 403 });
+ if (viewModel.DeclarationValue == null)
+ {
+ ModelState.AddModelError(nameof(viewModel.DeclarationValue), "Please select a declaration option");
+ return View(viewModel);
+ }
+ else if (viewModel.DeclarationValue == 1 && string.IsNullOrWhiteSpace(viewModel.CustomText))
+ {
+ ModelState.AddModelError(nameof(viewModel.CustomText), "Please enter the custom declaration text");
+ return View(viewModel);
+ }
+ else if (viewModel.DeclarationValue == 1 && viewModel.CustomText.Length > 2000)
+ {
+ ModelState.AddModelError(nameof(viewModel.CustomText), "Declaration text must be 2000 characters or fewer");
+ return View(viewModel);
+ }
+
+ var data = GetManagesupervisionData();
+ var model = new ManagesupervisionViewModel(viewModel, data.SupervisorDeclaration, data.Signoff);
+ model.CompetencyAssessmentName = viewModel.CompetencyAssessmentName;
+ SetManagesupervisionData(model);
+ return RedirectToAction("ManageSupervisionSettings", "CompetencyAssessments", new { viewModel.CompetencyAssessmentId });
+ }
+
+ [Route("/CompetencyAssessments/{competencyAssessmentId}/SupervisionSettings")]
+ [Route("/CompetencyAssessments/{competencyAssessmentId}/{actionName}/SupervisionSettings")]
+ public IActionResult ManageSupervisionSettings(int competencyAssessmentId, string? actionName)
+ {
+ var adminId = GetAdminID();
+ var baseData = competencyAssessmentService.GetCompetencyAssessmentBaseById(competencyAssessmentId, adminId);
+ var result = ValidateCompetencyAssessmentAndRole(competencyAssessmentId, adminId, "Supervisor Signoff Declaration", baseData);
+ if (result.StatusCode != 200)
+ return result;
+ var competencyAssessmentTaskStatus = competencyAssessmentService.GetCompetencyAssessmentTaskStatus(competencyAssessmentId, null);
+ if (actionName == "SupervisorRoles")
+ {
+ var model = new ManagesupervisionViewModel(competencyAssessmentId, baseData.CompetencyAssessmentName,
+ baseData.SupervisorResultsReview,
+ baseData.SupervisorSelfAssessmentReview,
+ baseData.SignOffSupervisorStatement,
+ baseData.SignOffRequestorStatement,
+ this.config.GetLearnerDefaultText(),
+ this.config.GetSupervisorDefaultText());
+ model.TaskCompleteChecked = competencyAssessmentTaskStatus.SupervisorRolesTaskStatus;
+ SetManagesupervisionData(model);
+ return View(model);
+ }
+ var data = GetManagesupervisionData();
+ var dataModel = new ManagesupervisionViewModel(competencyAssessmentId, data, this.config.GetLearnerDefaultText(), this.config.GetSupervisorDefaultText());
+ dataModel.TaskCompleteChecked = competencyAssessmentTaskStatus.SupervisorRolesTaskStatus;
+ return View(dataModel);
+
+ }
+
+ [HttpPost]
+ [Route("/CompetencyAssessments/{competencyAssessmentId}/SupervisionSettings")]
+ [Route("/CompetencyAssessments/{competencyAssessmentId}/{actionName}/SupervisionSettings")]
+ public IActionResult ManageSupervisionSettings(ManagesupervisionViewModel viewModel)
+ {
+ if (viewModel == null) return RedirectToAction("StatusCode", "LearningSolutions", new { code = 403 });
+ var data = GetManagesupervisionData();
+ if (!ModelState.IsValid)
+ {
+ var model = new ManagesupervisionViewModel(data.LearnerDeclaration, data.SupervisorDeclaration, data.Signoff);
+ return View("ManageSupervisionSettings", model);
+ }
+ var competencyAssessmentTaskStatus = competencyAssessmentService.GetCompetencyAssessmentTaskStatus(data.Signoff.CompetencyAssessmentId, null);
+ competencyAssessmentService.UpdateSupervisorRolesTaskStatus(data.Signoff.CompetencyAssessmentId, viewModel.TaskCompleteChecked ?? false);
+ competencyAssessmentService.UpdateSelfAssessments(data.Signoff.CompetencyAssessmentId,
+ data.Signoff.Signoff,
+ data.Signoff.Confirm,
+ data.SupervisorDeclaration.DeclarationValue,
+ SanitizerHelper.SanitizeHtmlData(data.SupervisorDeclaration.CustomText),
+ data.LearnerDeclaration.DeclarationValue,
+ SanitizerHelper.SanitizeHtmlData(data.LearnerDeclaration.CustomText)
+ );
+ multiPageFormService.ClearMultiPageFormData(MultiPageFormDataFeature.AddCustomWebForm("ManagesupervisionDataCWF"), TempData);
+ TempData.Clear();
+ return RedirectToAction("ManageCompetencyAssessment", new { competencyAssessmentId = viewModel.CompetencyAssessmentId });
+ }
+ [HttpGet]
+ [Route("/CompetencyAssessments/{competencyAssessmentId}/SendForReview")]
+ public IActionResult SendForReview(int competencyAssessmentId)
+ {
+ var adminId = GetAdminID();
+ var collaborators = competencyAssessmentService.GetCollaboratorsForCompetencyAssessmentId(competencyAssessmentId);
+ var competencyAssessmentBase = competencyAssessmentService.GetCompetencyAssessmentBaseById(competencyAssessmentId, adminId);
+ var result = ValidateCompetencyAssessmentAndRole(competencyAssessmentId, adminId, "Send For Review", competencyAssessmentBase);
+ if (result.StatusCode != 200)
+ return result;
+ var model = new SendForReviewViewModel(competencyAssessmentId, competencyAssessmentBase.CompetencyAssessmentName, collaborators, competencyAssessmentBase.PublishStatusID);
+ return View(model);
+ }
+ [HttpPost]
+ [Route("/CompetencyAssessments/{competencyAssessmentId}/SendForReview")]
+ public IActionResult SendForReview(SendForReviewViewModel send)
+ {
+ var adminId = GetAdminID();
+ if (!ModelState.IsValid || send.UserChecked == null || !send.UserChecked.Any())
+ {
+ send.Collaborators = competencyAssessmentService.GetCollaboratorsForCompetencyAssessmentId(send.CompetencyAssessmentID).Where(x => x.CompetencyAssessmentRole != "Owner");
+ ModelState.AddModelError("UserChecked", "You must select at least one user to send for review.");
+ return View(send);
+ }
+ var competencyAssessmentBase = competencyAssessmentService.GetCompetencyAssessmentBaseById(send.CompetencyAssessmentID, adminId);
+ var result = ValidateCompetencyAssessmentAndRole(send.CompetencyAssessmentID, adminId, "Send For Review", competencyAssessmentBase);
+ if (result.StatusCode != 200)
+ return result;
+ foreach (var collaborator in send.UserChecked)
+ {
+ var required = send.SignOffRequiredChecked.IndexOf(collaborator) != -1;
+ competencyAssessmentService.InsertSelfAssessmentReview(send.CompetencyAssessmentID, collaborator, required);
+ frameworkNotificationService.SendReviewRequestForCompetencyAssessment(collaborator, adminId, required, false, User.GetCentreIdKnownNotNull());
+ }
+ competencyAssessmentService.UpdateCompetencyAssessmentPublishStatus(send.CompetencyAssessmentID, 2, adminId);
+ var taskStatus = competencyAssessmentService.GetCompetencyAssessmentTaskStatus(send.CompetencyAssessmentID, null);
+ if (taskStatus.ReviewTaskStatus != true)
+ {
+ competencyAssessmentService.UpdateCompetencyAssessmentReviewTaskStatus(send.CompetencyAssessmentID, false);
+ }
+ return RedirectToAction("ManageCompetencyAssessment", new { competencyAssessmentId = send.CompetencyAssessmentID });
+
+ }
+ [HttpGet]
+ [Route("/CompetencyAssessments/{competencyAssessmentId}/PublishWithoutReview")]
+ public IActionResult PublishWithoutReview(int competencyAssessmentId)
+ {
+ var adminId = GetAdminID();
+ var competencyAssessmentBase = competencyAssessmentService.GetCompetencyAssessmentBaseById(competencyAssessmentId, adminId);
+ var result = ValidateCompetencyAssessmentAndRole(competencyAssessmentId, adminId, "Publish Without Review", competencyAssessmentBase);
+ if (result.StatusCode != 200)
+ return result;
+ var model = new PublishWithoutReviewViewModel(competencyAssessmentId, competencyAssessmentBase.CompetencyAssessmentName);
+ return View(model);
+ }
+ [HttpPost]
+ [Route("/CompetencyAssessments/{competencyAssessmentId}/PublishWithoutReview")]
+ public IActionResult PublishWithoutReview(PublishWithoutReviewViewModel publish)
+ {
+ var adminId = GetAdminID();
+ var competencyAssessmentBase = competencyAssessmentService.GetCompetencyAssessmentBaseById(publish.CompetencyAssessmentID, adminId);
+ var result = ValidateCompetencyAssessmentAndRole(publish.CompetencyAssessmentID, adminId, "Publish Without Review", competencyAssessmentBase);
+ if (result.StatusCode != 200)
+ return result;
+ if (competencyAssessmentBase.PublishStatusID == 3) return RedirectToAction("StatusCode", "LearningSolutions", new { code = 410 });
+ var taskStatus = competencyAssessmentService.GetCompetencyAssessmentTaskStatus(publish.CompetencyAssessmentID, null);
+ competencyAssessmentService.UpdateCompetencyAssessmentReviewTaskStatus(publish.CompetencyAssessmentID, true);
+ competencyAssessmentService.UpdateCompetencyAssessmentPublish(publish.CompetencyAssessmentID, 3, adminId, true, true);
+ return RedirectToAction("ManageCompetencyAssessment", new { competencyAssessmentId = publish.CompetencyAssessmentID });
+ }
+ [HttpGet]
+ [Route("/CompetencyAssessments/{competencyAssessmentId}/{selfAssessmentReviewId}/Review")]
+ public IActionResult SubmitReview(int competencyAssessmentId, int selfAssessmentReviewId)
+ {
+ var adminId = GetAdminID();
+ var competencyAssessmentBase = competencyAssessmentService.GetCompetencyAssessmentBaseById(competencyAssessmentId, adminId);
+ var selfAssessmentReview = competencyAssessmentService.GetCompetencySelfAssessmentReviewById(competencyAssessmentId, selfAssessmentReviewId);
+ var result = ValidateCompetencyAssessmentAndRole(competencyAssessmentId, adminId, "Submit Review", competencyAssessmentBase);
+ if (result.StatusCode != 200) return result;
+ if (selfAssessmentReview == null || selfAssessmentReview.SignedOff) return RedirectToAction("StatusCode", "LearningSolutions", new { code = 410 });
+ var model = new ViewModels.CompetencyAssessments.SubmitReviewViewModel(competencyAssessmentId, competencyAssessmentBase.CompetencyAssessmentName, selfAssessmentReview);
+ return View(model);
+ }
+ [HttpPost]
+ [Route("/CompetencyAssessments/{competencyAssessmentId}/{selfAssessmentReviewId}/Review")]
+ public IActionResult SubmitReview(SubmitReviewViewModel submit)
+ {
+ var adminId = GetAdminID();
+ var centreId = GetCentreId().GetValueOrDefault();
+ var competencyAssessmentBase = competencyAssessmentService.GetCompetencyAssessmentBaseById(submit.CompetencyAssessmentID, adminId);
+ int? commentId = null;
+ var result = ValidateCompetencyAssessmentAndRole(submit.CompetencyAssessmentID, adminId, "Submit Review", competencyAssessmentBase);
+ if (result.StatusCode != 200) return result;
+ if (string.IsNullOrWhiteSpace(submit.SelfAssessmentReview.Comment) || !submit.SelfAssessmentReview.SignedOff)
+ {
+ if (string.IsNullOrWhiteSpace(submit.SelfAssessmentReview.Comment))
+ {
+ ModelState.AddModelError(
+ "SelfAssessmentReview.Comment",
+ "Please enter a comment"
+ );
+ }
+
+ if (!submit.SelfAssessmentReview.SignedOff)
+ {
+ ModelState.AddModelError(
+ "SelfAssessmentReview.SignedOff",
+ "You must confirm your approval for the self-assessment to be published"
+ );
+ }
+
+ return View(submit);
+ }
+ commentId = competencyAssessmentService.InsertComment(submit.CompetencyAssessmentID, adminId, submit.SelfAssessmentReview.Comment, null);
+ competencyAssessmentService.UpdateSelfAssessmentReview(submit.CompetencyAssessmentID, submit.SelfAssessmentReview.ID, submit.SelfAssessmentReview.SignedOff, commentId);
+ frameworkNotificationService.SendCompetencyAssessmentsReviewOutcomeNotification(submit.SelfAssessmentReview.ID, centreId);
+ return RedirectToAction("ManageCompetencyAssessment", new { competencyAssessmentId = submit.CompetencyAssessmentID });
+ }
+ [HttpGet]
+ [Route("/CompetencyAssessments/{competencyAssessmentId}/PublishReview")]
+ public IActionResult PublishReview(int competencyAssessmentId)
+ {
+ var adminId = GetAdminID();
+ var competencyAssessmentBase = competencyAssessmentService.GetCompetencyAssessmentBaseById(competencyAssessmentId, adminId);
+ var result = ValidateCompetencyAssessmentAndRole(competencyAssessmentId, adminId, "Publish Without Review", competencyAssessmentBase);
+ if (result.StatusCode != 200)
+ return result;
+ var competencyAssessmentIdReviews = competencyAssessmentService.GetCompetencySelfAssessmentReviews(competencyAssessmentId);
+ var model = new PublishReviewViewModel(competencyAssessmentId, competencyAssessmentBase.CompetencyAssessmentName, competencyAssessmentIdReviews, competencyAssessmentBase);
+ return View(model);
+ }
+ public IActionResult PublishSelfAssesment(int competencyAssessmentId)
+ {
+ var adminId = GetAdminID();
+ var competencyAssessmentBase = competencyAssessmentService.GetCompetencyAssessmentBaseById(competencyAssessmentId, adminId);
+ var result = ValidateCompetencyAssessmentAndRole(competencyAssessmentId, adminId, "Publish Without Review", competencyAssessmentBase);
+ if (result.StatusCode != 200)
+ return result;
+ if (competencyAssessmentBase.PublishStatusID == 3) return RedirectToAction("StatusCode", "LearningSolutions", new { code = 410 });
+ var taskStatus = competencyAssessmentService.GetCompetencyAssessmentTaskStatus(competencyAssessmentId, null);
+ competencyAssessmentService.UpdateCompetencyAssessmentReviewTaskStatus(competencyAssessmentId, true);
+ competencyAssessmentService.UpdateCompetencyAssessmentPublish(competencyAssessmentId, 3, adminId, true, true);
+ return RedirectToAction("ManageCompetencyAssessment", new { competencyAssessmentId = competencyAssessmentId });
+ }
+ public IActionResult ResendRequest(int reviewId, int competencyAssessmentId, int competencyAssessmentCollaboratorId, bool required)
+ {
+ var adminId = GetAdminID();
+ frameworkNotificationService.SendReviewRequestForCompetencyAssessment(competencyAssessmentCollaboratorId, adminId, required, true, User.GetCentreIdKnownNotNull());
+ competencyAssessmentService.UpdateReviewRequestedDate(reviewId);
+ return RedirectToAction("PublishReview", new { competencyAssessmentId });
+ }
+ public IActionResult RequestReReview(int competencyAssessmentId, int reviewId)
+ {
+ var adminId = GetAdminID();
+ competencyAssessmentService.InsertCompetencySelfAssessmentReview(reviewId);
+ var review = competencyAssessmentService.GetSelfAssessmentReviewNotification(reviewId);
+ if (review == null) return StatusCode(404);
+ frameworkNotificationService.SendReviewRequestForCompetencyAssessment(review.SelfAssessmentCollaboratorID, adminId, review.SignOffRequired, false, User.GetCentreIdKnownNotNull());
+ return RedirectToAction("PublishReview", new { competencyAssessmentId });
+ }
+ public IActionResult RemoveRequest(int competencyAssessmentId, int reviewId)
+ {
+ competencyAssessmentService.ArchiveSelfAssessmentReviewRequest(reviewId);
+ return RedirectToAction("PublishReview", new { competencyAssessmentId });
+ }
+
+ [Route("/CompetencyAssessments/{competencyAssessmentId}/Preview")]
+ public IActionResult CompetencyAssessmentPreview(int competencyAssessmentId = 0)
+ {
+ var adminId = GetAdminID();
+ var userId = (int)User.GetUserId();
+ var centreId = (int)GetCentreId();
+ var candidateId = User.GetCandidateIdKnownNotNull();
+ var competencyAssessmentBase = new CompetencyAssessmentBase();
+
+ if (competencyAssessmentId <= 0)
+ {
+ return StatusCode(500);
+ }
+ competencyAssessmentBase = competencyAssessmentService.GetCompetencyAssessmentBaseById(competencyAssessmentId, adminId);
+ var result = ValidateCompetencyAssessmentAndRole(competencyAssessmentId, adminId, "Competency Assessment Name", competencyAssessmentBase);
+ if (result.StatusCode != 200)
+ return result;
+
+ if (selfAssessmentService.CanDelegateAccessSelfAssessment(userId, competencyAssessmentId, centreId))
+ {
+ return RedirectToAction("SelfAssessment", "LearningPortal", new { selfAssessmentId = competencyAssessmentId });
+ }
+ else
+ {
+ return RedirectToAction("PreviewConfirm", new { competencyAssessmentId });
+ }
+ }
+
+ [Route("/CompetencyAssessments/{competencyAssessmentId}/Preview/Confirm")]
+ public IActionResult PreviewConfirm(int competencyAssessmentId)
+ {
+ if (competencyAssessmentId <= 0)
+ {
+ return StatusCode(500);
+ }
+
+ var adminId = GetAdminID();
+ var userId = (int)User.GetUserId();
+ var centreId = (int)GetCentreId();
+ var centreName = centresService.GetCentreName(centreId);
+ var competencyAssessmentBase = new CompetencyAssessmentBase();
+
+ competencyAssessmentBase = competencyAssessmentService.GetCompetencyAssessmentBaseById(competencyAssessmentId, adminId);
+ var result = ValidateCompetencyAssessmentAndRole(competencyAssessmentId, adminId, "Competency Assessment Preview Confirm", competencyAssessmentBase);
+ if (result.StatusCode != 200)
+ return result;
+
+ if (selfAssessmentService.CanDelegateAccessSelfAssessment(userId, competencyAssessmentId, centreId))
+ {
+ return StatusCode(500);
+ }
+
+ var model = new CompetencyAssessmentPreviewViewModel(competencyAssessmentId, centreId, centreName);
+
+ return View("CompetencyAssessmentPreviewConfirm", model);
+ }
+
+ [HttpPost]
+ [Route("/CompetencyAssessments/{competencyAssessmentId}/Preview/Confirm")]
+ public IActionResult PreviewConfirm(CompetencyAssessmentPreviewViewModel model)
+ {
+ var adminId = GetAdminID();
+ var userId = User.GetUserIdKnownNotNull();
+ var centreId = (int)GetCentreId();
+ var userEmail = User.GetUserPrimaryEmail();
+ var candidateId = User.GetCandidateIdKnownNotNull();
+
+ centreSelfAssessmentsService.InsertCentreSelfAssessment(centreId, model.CompetencyAssessmentId, false);
+
+ enrolService.EnrolOnActivitySelfAssessment(model.CompetencyAssessmentId, candidateId, 0, userEmail, 0, null, userId, centreId, adminId, true);
+
+ return RedirectToAction("SelfAssessment", "LearningPortal", new { selfAssessmentId = model.CompetencyAssessmentId });
+ }
+
+ private void SetManagesupervisionData(ManagesupervisionViewModel data)
+ {
+ multiPageFormService.SetMultiPageFormData(
+ data,
+ MultiPageFormDataFeature.AddCustomWebForm("ManagesupervisionDataCWF"),
+ TempData
+ );
+ }
+ private ManagesupervisionViewModel GetManagesupervisionData()
+ {
+ var data = multiPageFormService.GetMultiPageFormData(
+ MultiPageFormDataFeature.AddCustomWebForm("ManagesupervisionDataCWF"),
+ TempData
+ ).GetAwaiter().GetResult();
+ return data;
+ }
+ private void SetcompetencyAssessmentFeaturesData(CompetencyAssessmentFeaturesViewModel data)
+ {
+ multiPageFormService.SetMultiPageFormData(
+ data,
+ MultiPageFormDataFeature.AddCustomWebForm("AssessmentFeaturesDataCWF"),
+ TempData
+ );
+ }
+ private CompetencyAssessmentFeaturesViewModel GetcompetencyAssessmentFeaturesData()
+ {
+ var data = multiPageFormService.GetMultiPageFormData(
+ MultiPageFormDataFeature.AddCustomWebForm("AssessmentFeaturesDataCWF"),
+ TempData
+ ).GetAwaiter().GetResult();
+ return data;
+ }
+
+ private void SetOptionsLabelsData(OptionsLabelsViewModel data)
+ {
+ multiPageFormService.SetMultiPageFormData(
+ data,
+ MultiPageFormDataFeature.AddCustomWebForm("OptionsLabelsCWF"),
+ TempData
+ );
+ }
+
+ private OptionsLabelsViewModel GetOptionsLabelslData()
+ {
+ var data = multiPageFormService.GetMultiPageFormData(
+ MultiPageFormDataFeature.AddCustomWebForm("OptionsLabelsCWF"),
+ TempData
+ ).GetAwaiter().GetResult();
+ return data;
+ }
+ private bool ValidateStep(OptionsLabelsViewModel data, ref int step)
+ {
+ int original = step;
+ if (step == (int)OptionLabel.Declaration && !data.IsSupervisionSwitchedOn) step = (int)OptionLabel.Signposting;
+ if (step == (int)OptionLabel.Signposting && !data.IsSignpostedLearning) step = (int)OptionLabel.LinearNavigation;
+ if (step == (int)OptionLabel.CommentsLabel && !data.IsSupervisionSwitchedOn) step = (int)OptionLabel.Summary;
+
+ return step != original;
+ }
+
+ private StatusCodeResult ValidateCompetencyAssessmentAndRole(int competencyAssessmentId, int adminId, string pageName, CompetencyAssessmentBase? competencyAssessmentBase = null)
+ {
+ if (competencyAssessmentId > 0)
+ {
+ competencyAssessmentBase ??= competencyAssessmentService.GetCompetencyAssessmentBaseById(competencyAssessmentId, adminId);
+ if (competencyAssessmentBase == null)
+ {
+ logger.LogWarning($"Failed to load {pageName} page for competencyAssessmentId: {competencyAssessmentId} adminId: {adminId}");
+ return StatusCode(500);
+ }
+ if (competencyAssessmentBase.UserRole < 2)
+ {
+ return StatusCode(403);
+ }
+ }
+ else
+ {
+ return StatusCode(500);
+ }
+ return StatusCode(200);
+ }
+ }
+}
diff --git a/DigitalLearningSolutions.Web/Controllers/CompetencyAssessmentsController/CompetencyAssessmentsController.cs b/DigitalLearningSolutions.Web/Controllers/CompetencyAssessmentsController/CompetencyAssessmentsController.cs
new file mode 100644
index 0000000000..031ec28ec7
--- /dev/null
+++ b/DigitalLearningSolutions.Web/Controllers/CompetencyAssessmentsController/CompetencyAssessmentsController.cs
@@ -0,0 +1,86 @@
+namespace DigitalLearningSolutions.Web.Controllers.CompetencyAssessmentsController
+{
+ using DigitalLearningSolutions.Web.Helpers;
+ using DigitalLearningSolutions.Web.Services;
+ using GDS.MultiPageFormData;
+ using Microsoft.AspNetCore.Authorization;
+ using Microsoft.AspNetCore.Mvc;
+ using Microsoft.Extensions.Configuration;
+ using Microsoft.Extensions.Logging;
+
+ [Authorize(Policy = CustomPolicies.UserFrameworksAdminOnly)]
+ public partial class CompetencyAssessmentsController : Controller
+ {
+ private readonly ICompetencyAssessmentService competencyAssessmentService;
+ private readonly IFrameworkService frameworkService;
+ private readonly ICommonService commonService;
+ private readonly IFrameworkNotificationService frameworkNotificationService;
+ private readonly ISelfAssessmentNotificationService selfAssessmentNotificationService;
+ private readonly ISelfAssessmentService selfAssessmentService;
+ private readonly ICentresService centresService;
+ private readonly ICentreSelfAssessmentsService centreSelfAssessmentsService;
+ private readonly IEnrolService enrolService;
+ private readonly ILogger logger;
+ private readonly IConfiguration config;
+ private readonly IMultiPageFormService multiPageFormService;
+ public CompetencyAssessmentsController(
+ ICompetencyAssessmentService competencyAssessmentService,
+ IFrameworkService frameworkService,
+ ICommonService commonService,
+ IFrameworkNotificationService frameworkNotificationService,
+ ISelfAssessmentNotificationService selfAssessmentNotificationService,
+ ISelfAssessmentService selfAssessmentService,
+ ICentresService centresService,
+ ICentreSelfAssessmentsService centreSelfAssessmentsService,
+ IEnrolService enrolService,
+ ILogger logger,
+ IConfiguration config,
+ IMultiPageFormService multiPageFormService)
+ {
+ this.competencyAssessmentService = competencyAssessmentService;
+ this.frameworkService = frameworkService;
+ this.commonService = commonService;
+ this.frameworkNotificationService = frameworkNotificationService;
+ this.selfAssessmentNotificationService = selfAssessmentNotificationService;
+ this.selfAssessmentService = selfAssessmentService;
+ this.centresService = centresService;
+ this.centreSelfAssessmentsService = centreSelfAssessmentsService;
+ this.enrolService = enrolService;
+ this.logger = logger;
+ this.config = config;
+ this.multiPageFormService = multiPageFormService;
+ }
+ public IActionResult Index()
+ {
+ return View();
+ }
+ private int? GetCentreId()
+ {
+ return User.GetCustomClaimAsInt(CustomClaimTypes.UserCentreId);
+ }
+ private int GetAdminID()
+ {
+ return User.GetCustomClaimAsRequiredInt(CustomClaimTypes.UserAdminId);
+ }
+ private bool GetIsWorkforceManager()
+ {
+ var isWorkforceManager = User.GetCustomClaimAsBool(CustomClaimTypes.IsWorkforceManager);
+ return isWorkforceManager != null && (bool)isWorkforceManager;
+ }
+ private bool GetIsWorkforceContributor()
+ {
+ var isWorkforceContributor = User.GetCustomClaimAsBool(CustomClaimTypes.IsWorkforceContributor);
+ return isWorkforceContributor != null && (bool)isWorkforceContributor;
+ }
+ private bool GetIsFrameworkDeveloper()
+ {
+ var isFrameworkDeveloper = User.GetCustomClaimAsBool(CustomClaimTypes.IsFrameworkDeveloper);
+ return isFrameworkDeveloper != null && (bool)isFrameworkDeveloper;
+ }
+ private bool GetIsFrameworkContributor()
+ {
+ var isFrameworkContributor = User.GetCustomClaimAsBool(CustomClaimTypes.IsFrameworkContributor);
+ return isFrameworkContributor != null && (bool)isFrameworkContributor;
+ }
+ }
+}
diff --git a/DigitalLearningSolutions.Web/Controllers/FrameworksController/Competencies.cs b/DigitalLearningSolutions.Web/Controllers/FrameworksController/Competencies.cs
index 01c95404e3..19a918296f 100644
--- a/DigitalLearningSolutions.Web/Controllers/FrameworksController/Competencies.cs
+++ b/DigitalLearningSolutions.Web/Controllers/FrameworksController/Competencies.cs
@@ -68,11 +68,11 @@ public IActionResult AddEditFrameworkCompetencyGroup(int frameworkId, Competency
if (string.IsNullOrWhiteSpace(StringHelper.StripHtmlTags(competencyGroupBase?.Description))) { competencyGroupBase.Description = null; }
if (competencyGroupBase.ID > 0)
{
- frameworkService.UpdateFrameworkCompetencyGroup(frameworkCompetencyGroupId, competencyGroupBase.CompetencyGroupID, competencyGroupBase.Name, SanitizerHelper.SanitizeHtmlData
+ frameworkService.UpdateFrameworkCompetencyGroup(frameworkCompetencyGroupId, competencyGroupBase.CompetencyGroupID, competencyGroupBase.Name.Trim(), SanitizerHelper.SanitizeHtmlData
(competencyGroupBase.Description), adminId);
return new RedirectResult(Url.Action("ViewFramework", new { tabname = "Structure", frameworkId }) + "#fcgroup-" + frameworkCompetencyGroupId.ToString());
}
- var newCompetencyGroupId = frameworkService.InsertCompetencyGroup(competencyGroupBase.Name, SanitizerHelper.SanitizeHtmlData(competencyGroupBase.Description), adminId, frameworkId);
+ var newCompetencyGroupId = frameworkService.InsertCompetencyGroup(competencyGroupBase.Name.Trim(), SanitizerHelper.SanitizeHtmlData(competencyGroupBase.Description), adminId, frameworkId);
if (newCompetencyGroupId > 0)
{
var newFrameworkCompetencyGroupId = frameworkService.InsertFrameworkCompetencyGroup(newCompetencyGroupId, frameworkId, adminId);
@@ -159,6 +159,7 @@ public IActionResult AddEditFrameworkCompetency(int frameworkId, FrameworkCompet
FrameworkCompetency = frameworkCompetency,
CompetencyFlags = competencyFlags
};
+ ModelState.Remove("SearchableName");
return View("Developer/Competency", model);
}
var adminId = GetAdminId();
@@ -168,7 +169,7 @@ public IActionResult AddEditFrameworkCompetency(int frameworkId, FrameworkCompet
{
- frameworkService.UpdateFrameworkCompetency(frameworkCompetencyId, frameworkCompetency.Name, frameworkCompetency.Description, adminId);
+ frameworkService.UpdateFrameworkCompetency(frameworkCompetencyId, frameworkCompetency.Name.Trim(), frameworkCompetency.Description, adminId);
frameworkService.UpdateCompetencyFlags(frameworkId, frameworkCompetency.CompetencyID, selectedFlagIds);
return new RedirectResult(Url.Action("ViewFramework", new { tabname = "Structure", frameworkId, frameworkCompetencyGroupId, frameworkCompetencyId }) + "#fc-" + frameworkCompetencyId.ToString());
}
@@ -215,7 +216,7 @@ public IActionResult AddDuplicateCompetency(int frameworkId, string competencyNa
private IActionResult SaveCompetency(int adminId, int frameworkId, FrameworkCompetency frameworkCompetency, int frameworkCompetencyId, int? frameworkCompetencyGroupId, int[]? selectedFlagIds)
{
- var newCompetencyId = frameworkService.InsertCompetency(frameworkCompetency.Name, frameworkCompetency.Description, adminId);
+ var newCompetencyId = frameworkService.InsertCompetency(frameworkCompetency.Name.Trim(), frameworkCompetency.Description, adminId);
if (newCompetencyId > 0)
{
var newFrameworkCompetencyId = frameworkService.InsertFrameworkCompetency(newCompetencyId, frameworkCompetencyGroupId, adminId, frameworkId);
diff --git a/DigitalLearningSolutions.Web/Controllers/FrameworksController/Frameworks.cs b/DigitalLearningSolutions.Web/Controllers/FrameworksController/Frameworks.cs
index 9fb75fdc67..a7729069ec 100644
--- a/DigitalLearningSolutions.Web/Controllers/FrameworksController/Frameworks.cs
+++ b/DigitalLearningSolutions.Web/Controllers/FrameworksController/Frameworks.cs
@@ -137,18 +137,18 @@ public IActionResult StartNewFrameworkSession()
MultiPageFormDataFeature.AddNewFramework,
TempData
);
- return RedirectToAction("CreateNewFramework", "Frameworks", new { actionname = "New" });
+ return RedirectToAction("CreateNewFramework", "Frameworks", new { actionName = "New" });
}
- [Route("/Frameworks/Name/{actionname}/{frameworkId}")]
- [Route("/Frameworks/Name/{actionname}")]
+ [Route("/Frameworks/Name/{actionName}/{frameworkId}")]
+ [Route("/Frameworks/Name/{actionName}")]
[SetSelectedTab(nameof(NavMenuTab.Frameworks))]
[ResponseCache(CacheProfileName = "Never")]
[TypeFilter(
typeof(RedirectToErrorEmptySessionData),
Arguments = new object[] { nameof(MultiPageFormDataFeature.AddNewFramework) }
)]
- public IActionResult CreateNewFramework(string actionname, int frameworkId = 0)
+ public IActionResult CreateNewFramework(string actionName, int frameworkId = 0)
{
var adminId = GetAdminId();
DetailFramework? detailFramework;
@@ -174,15 +174,15 @@ public IActionResult CreateNewFramework(string actionname, int frameworkId = 0)
return View("Developer/Name", detailFramework);
}
[HttpPost]
- [Route("/Frameworks/Name/{actionname}/{frameworkId}")]
- [Route("/Frameworks/Name/{actionname}")]
+ [Route("/Frameworks/Name/{actionName}/{frameworkId}")]
+ [Route("/Frameworks/Name/{actionName}")]
[SetSelectedTab(nameof(NavMenuTab.Frameworks))]
[ResponseCache(CacheProfileName = "Never")]
[TypeFilter(
typeof(RedirectToErrorEmptySessionData),
Arguments = new object[] { nameof(MultiPageFormDataFeature.AddNewFramework) }
)]
- public IActionResult CreateNewFramework(DetailFramework detailFramework, string actionname, int frameworkId = 0)
+ public IActionResult CreateNewFramework(DetailFramework detailFramework, string actionName, int frameworkId = 0)
{
if (!ModelState.IsValid)
{
@@ -190,7 +190,7 @@ public IActionResult CreateNewFramework(DetailFramework detailFramework, string
ModelState.AddModelError(nameof(BaseFramework.FrameworkName), "Please enter a valid framework name (between 3 and 255 characters)");
return View("Developer/Name", detailFramework);
}
- if (actionname == "New")
+ if (actionName == "New")
{
SessionNewFramework sessionNewFramework = multiPageFormService.GetMultiPageFormData(MultiPageFormDataFeature.AddNewFramework, TempData).GetAwaiter().GetResult();
sessionNewFramework.DetailFramework = detailFramework;
@@ -199,7 +199,7 @@ public IActionResult CreateNewFramework(DetailFramework detailFramework, string
MultiPageFormDataFeature.AddNewFramework,
TempData
);
- return RedirectToAction("SetNewFrameworkName", new { frameworkname = detailFramework.FrameworkName, actionname });
+ return RedirectToAction("SetNewFrameworkName", new { frameworkname = detailFramework.FrameworkName, actionName });
}
var adminId = GetAdminId();
var isUpdated = frameworkService.UpdateFrameworkName(detailFramework.ID, adminId, detailFramework.FrameworkName);
@@ -208,10 +208,10 @@ public IActionResult CreateNewFramework(DetailFramework detailFramework, string
return View("Developer/Name", detailFramework);
}
- [Route("/Frameworks/Similar/{actionname}")]
- public IActionResult SetNewFrameworkName(string frameworkname, string actionname)
+ [Route("/Frameworks/Similar/{actionName}")]
+ public IActionResult SetNewFrameworkName(string frameworkname, string actionName)
{
- if (actionname == "New")
+ if (actionName == "New")
{
var sessionNewFramework = multiPageFormService.GetMultiPageFormData(MultiPageFormDataFeature.AddNewFramework, TempData).GetAwaiter().GetResult();
multiPageFormService.SetMultiPageFormData(
@@ -242,14 +242,14 @@ public IActionResult SetNewFrameworkName(string frameworkname, string actionname
};
return View("Developer/Similar", model);
}
- return RedirectToAction("SaveNewFramework", "Frameworks", new { frameworkname, actionname });
+ return RedirectToAction("SaveNewFramework", "Frameworks", new { frameworkname, actionName });
}
- public IActionResult SaveNewFramework(string frameworkname, string actionname)
+ public IActionResult SaveNewFramework(string frameworkname, string actionName)
{
//var framework = frameworkService.CreateFramework(frameworkname, GetAdminID());
//need to create framework and move to next step.
- if (actionname != "New")
+ if (actionName != "New")
{
return StatusCode(500);
}
@@ -259,23 +259,23 @@ public IActionResult SaveNewFramework(string frameworkname, string actionname)
MultiPageFormDataFeature.AddNewFramework,
TempData
);
- return RedirectToAction("FrameworkDescription", "Frameworks", new { actionname });
+ return RedirectToAction("FrameworkDescription", "Frameworks", new { actionName });
}
- [Route("/Frameworks/Description/{actionname}/")]
- [Route("/Frameworks/Description/{actionname}/{frameworkId}/")]
+ [Route("/Frameworks/Description/{actionName}/")]
+ [Route("/Frameworks/Description/{actionName}/{frameworkId}/")]
[SetSelectedTab(nameof(NavMenuTab.Frameworks))]
[ResponseCache(CacheProfileName = "Never")]
[TypeFilter(
typeof(RedirectToErrorEmptySessionData),
Arguments = new object[] { nameof(MultiPageFormDataFeature.AddNewFramework) }
)]
- public IActionResult FrameworkDescription(string actionname, int frameworkId = 0)
+ public IActionResult FrameworkDescription(string actionName, int frameworkId = 0)
{
var adminId = GetAdminId();
var centreId = GetCentreId();
DetailFramework? framework;
- if (actionname == "New")
+ if (actionName == "New")
{
var sessionNewFramework = multiPageFormService.GetMultiPageFormData(MultiPageFormDataFeature.AddNewFramework, TempData).GetAwaiter().GetResult();
framework = sessionNewFramework.DetailFramework;
@@ -299,16 +299,16 @@ public IActionResult FrameworkDescription(string actionname, int frameworkId = 0
}
[HttpPost]
- [Route("/Frameworks/Description/{actionname}/")]
- [Route("/Frameworks/Description/{actionname}/{frameworkId}/")]
+ [Route("/Frameworks/Description/{actionName}/")]
+ [Route("/Frameworks/Description/{actionName}/{frameworkId}/")]
[ResponseCache(CacheProfileName = "Never")]
[TypeFilter(
typeof(RedirectToErrorEmptySessionData),
Arguments = new object[] { nameof(MultiPageFormDataFeature.AddNewFramework) }
)]
- public IActionResult FrameworkDescription(DetailFramework detailFramework, string actionname, int frameworkId = 0)
+ public IActionResult FrameworkDescription(DetailFramework detailFramework, string actionName, int frameworkId = 0)
{
- if (actionname == "New")
+ if (actionName == "New")
{
var sessionNewFramework = multiPageFormService.GetMultiPageFormData(MultiPageFormDataFeature.AddNewFramework, TempData).GetAwaiter().GetResult();
sessionNewFramework.DetailFramework = detailFramework;
@@ -317,7 +317,7 @@ public IActionResult FrameworkDescription(DetailFramework detailFramework, strin
MultiPageFormDataFeature.AddNewFramework,
TempData
);
- return RedirectToAction("FrameworkType", "Frameworks", new { actionname });
+ return RedirectToAction("FrameworkType", "Frameworks", new { actionName });
}
detailFramework.Description = SanitizerHelper.SanitizeHtmlData(detailFramework.Description);
frameworkService.UpdateFrameworkDescription(frameworkId, GetAdminId(), detailFramework.Description);
@@ -325,20 +325,20 @@ public IActionResult FrameworkDescription(DetailFramework detailFramework, strin
}
- [Route("/Frameworks/Type/{actionname}/")]
- [Route("/Frameworks/Type/{actionname}/{frameworkId}/")]
+ [Route("/Frameworks/Type/{actionName}/")]
+ [Route("/Frameworks/Type/{actionName}/{frameworkId}/")]
[SetSelectedTab(nameof(NavMenuTab.Frameworks))]
[ResponseCache(CacheProfileName = "Never")]
[TypeFilter(
typeof(RedirectToErrorEmptySessionData),
Arguments = new object[] { nameof(MultiPageFormDataFeature.AddNewFramework) }
)]
- public IActionResult FrameworkType(string actionname, int frameworkId = 0)
+ public IActionResult FrameworkType(string actionName, int frameworkId = 0)
{
var adminId = GetAdminId();
var centreId = GetCentreId();
DetailFramework? framework;
- if (actionname == "New")
+ if (actionName == "New")
{
var sessionNewFramework = multiPageFormService.GetMultiPageFormData(MultiPageFormDataFeature.AddNewFramework, TempData).GetAwaiter().GetResult();
framework = sessionNewFramework.DetailFramework;
@@ -362,16 +362,16 @@ public IActionResult FrameworkType(string actionname, int frameworkId = 0)
}
[HttpPost]
- [Route("/Frameworks/Type/{actionname}/")]
- [Route("/Frameworks/Type/{actionname}/{frameworkId}/")]
+ [Route("/Frameworks/Type/{actionName}/")]
+ [Route("/Frameworks/Type/{actionName}/{frameworkId}/")]
[ResponseCache(CacheProfileName = "Never")]
[TypeFilter(
typeof(RedirectToErrorEmptySessionData),
Arguments = new object[] { nameof(MultiPageFormDataFeature.AddNewFramework) }
)]
- public IActionResult FrameworkType(DetailFramework detailFramework, string actionname, int frameworkId = 0)
+ public IActionResult FrameworkType(DetailFramework detailFramework, string actionName, int frameworkId = 0)
{
- if (actionname == "New")
+ if (actionName == "New")
{
var sessionNewFramework = multiPageFormService.GetMultiPageFormData(MultiPageFormDataFeature.AddNewFramework, TempData).GetAwaiter().GetResult();
sessionNewFramework.DetailFramework = detailFramework;
@@ -380,26 +380,26 @@ public IActionResult FrameworkType(DetailFramework detailFramework, string actio
MultiPageFormDataFeature.AddNewFramework,
TempData
);
- return RedirectToAction("SetNewFrameworkBrand", "Frameworks", new { actionname });
+ return RedirectToAction("SetNewFrameworkBrand", "Frameworks", new { actionName });
}
frameworkService.UpdateFrameworkConfig(frameworkId, GetAdminId(), detailFramework.FrameworkConfig);
return RedirectToAction("ViewFramework", new { tabname = "Details", frameworkId });
}
- [Route("/Frameworks/Categorise/{actionname}/")]
- [Route("/Frameworks/Categorise/{actionname}/{frameworkId}/")]
+ [Route("/Frameworks/Categorise/{actionName}/")]
+ [Route("/Frameworks/Categorise/{actionName}/{frameworkId}/")]
[SetSelectedTab(nameof(NavMenuTab.Frameworks))]
[ResponseCache(CacheProfileName = "Never")]
[TypeFilter(
typeof(RedirectToErrorEmptySessionData),
Arguments = new object[] { nameof(MultiPageFormDataFeature.AddNewFramework) }
)]
- public IActionResult SetNewFrameworkBrand(string actionname, int frameworkId = 0)
+ public IActionResult SetNewFrameworkBrand(string actionName, int frameworkId = 0)
{
var adminId = GetAdminId();
var centreId = GetCentreId();
DetailFramework? framework;
- if (actionname == "New")
+ if (actionName == "New")
{
if (TempData[MultiPageFormDataFeature.AddNewFramework.TempDataKey] == null)
{
@@ -444,13 +444,13 @@ public IActionResult SetNewFrameworkBrand(string actionname, int frameworkId = 0
}
[HttpPost]
- [Route("/Frameworks/Categorise/{actionname}/")]
- [Route("/Frameworks/Categorise/{actionname}/{frameworkId}/")]
- public IActionResult SetNewFrameworkBrand(DetailFramework? detailFramework, string actionname, int frameworkId = 0)
+ [Route("/Frameworks/Categorise/{actionName}/")]
+ [Route("/Frameworks/Categorise/{actionName}/{frameworkId}/")]
+ public IActionResult SetNewFrameworkBrand(DetailFramework? detailFramework, string actionName, int frameworkId = 0)
{
var adminId = GetAdminId();
var centreId = GetCentreId();
- if (actionname != "New" && frameworkId > 0)
+ if (actionName != "New" && frameworkId > 0)
{
detailFramework = InsertBrandingCategoryTopicIfRequired(detailFramework);
if (detailFramework == null) return RedirectToAction("SetNewFrameworkBrand", "Frameworks", new { frameworkId });
@@ -578,8 +578,8 @@ public IActionResult RemoveFrameworkFlag(RemoveCustomFlagConfirmationViewModel m
}
[HttpPost]
- [Route("/Frameworks/Flags/{actionname}/{frameworkId}/{flagId}")]
- public IActionResult EditFrameworkFlag(CustomFlagViewModel model, int frameworkId, string actionname, int flagId)
+ [Route("/Frameworks/Flags/{actionName}/{frameworkId}/{flagId}")]
+ public IActionResult EditFrameworkFlag(CustomFlagViewModel model, int frameworkId, string actionName, int flagId)
{
if (ModelState.IsValid)
{
@@ -589,7 +589,7 @@ public IActionResult EditFrameworkFlag(CustomFlagViewModel model, int frameworkI
bool nameExists = flags.Any(x => x.FlagName?.Trim().ToLower() == model.FlagName?.Trim().ToLower());
bool idExists = flags.Any(x => x.FlagId == flagId);
- if (actionname == "Edit")
+ if (actionName == "Edit")
{
if (nameExists && !idExists)
{
@@ -616,11 +616,11 @@ public IActionResult EditFrameworkFlag(CustomFlagViewModel model, int frameworkI
}
[HttpGet]
- [Route("/Frameworks/Flags/{actionname:regex(Edit|New)}/{frameworkId}/{flagId}")]
- public IActionResult EditFrameworkFlag(int frameworkId, string actionname, int flagId)
+ [Route("/Frameworks/Flags/{actionName:regex(Edit|New)}/{frameworkId}/{flagId}")]
+ public IActionResult EditFrameworkFlag(int frameworkId, string actionName, int flagId)
{
var model = new CustomFlagViewModel();
- if (actionname == "Edit")
+ if (actionName == "Edit")
{
var flag = frameworkService.GetCustomFlagsByFrameworkId(frameworkId, (int?)flagId).FirstOrDefault();
model = new CustomFlagViewModel()
@@ -634,9 +634,9 @@ public IActionResult EditFrameworkFlag(int frameworkId, string actionname, int f
return View("Developer/EditCustomFlag", model);
}
- [Route("/Frameworks/Collaborators/{actionname}/{frameworkId}/")]
+ [Route("/Frameworks/Collaborators/{actionName}/{frameworkId}/")]
[ResponseCache(CacheProfileName = "Never")]
- public IActionResult AddCollaborators(string actionname, int frameworkId, bool error = false)
+ public IActionResult AddCollaborators(string actionName, int frameworkId, bool error = false)
{
var adminId = GetAdminId();
var collaborators = frameworkService.GetCollaboratorsForFrameworkId(frameworkId);
@@ -658,15 +658,15 @@ public IActionResult AddCollaborators(string actionname, int frameworkId, bool e
}
[HttpPost]
- [Route("/Frameworks/Collaborators/{actionname}/{frameworkId}/")]
- public IActionResult AddCollaborator(string actionname, string userEmail, bool canModify, int frameworkId)
+ [Route("/Frameworks/Collaborators/{actionName}/{frameworkId}/")]
+ public IActionResult AddCollaborator(string actionName, string userEmail, bool canModify, int frameworkId)
{
int? centreID = GetCentreId();
var collaboratorId = frameworkService.AddCollaboratorToFramework(frameworkId, userEmail, canModify, centreID);
if (collaboratorId > 0)
{
frameworkNotificationService.SendFrameworkCollaboratorInvite(collaboratorId, GetAdminId());
- return RedirectToAction("AddCollaborators", "Frameworks", new { frameworkId, actionname });
+ return RedirectToAction("AddCollaborators", "Frameworks", new { frameworkId, actionName });
}
else
{
@@ -691,15 +691,15 @@ public IActionResult AddCollaborator(string actionname, string userEmail, bool c
{
TempData["FrameworkError"] = "User not added,Kindly try again;";
}
- return RedirectToAction("AddCollaborators", "Frameworks", new { frameworkId, actionname });
+ return RedirectToAction("AddCollaborators", "Frameworks", new { frameworkId, actionName });
}
}
- public IActionResult RemoveCollaborator(int frameworkId, string actionname, int id)
+ public IActionResult RemoveCollaborator(int frameworkId, string actionName, int id)
{
frameworkService.RemoveCollaboratorFromFramework(frameworkId, id);
- return RedirectToAction("AddCollaborators", "Frameworks", new { frameworkId, actionname });
+ return RedirectToAction("AddCollaborators", "Frameworks", new { frameworkId, actionName });
}
[Route("/Framework/{frameworkId}/{tabname}/{frameworkCompetencyGroupId}/{frameworkCompetencyId}")]
@@ -724,9 +724,9 @@ public IActionResult ViewFramework(string tabname, int frameworkId, int? framewo
model.TabNavLinks = new TabsNavViewModel(FrameworkTab.Details, routeData);
break;
case "Structure":
- model.FrameworkCompetencyGroups = frameworkService.GetFrameworkCompetencyGroups(frameworkId).ToList();
+ model.FrameworkCompetencyGroups = frameworkService.GetFrameworkCompetencyGroups(frameworkId, null).ToList();
model.CompetencyFlags = frameworkService.GetCompetencyFlagsByFrameworkId(frameworkId, null, selected: true);
- model.FrameworkCompetencies = frameworkService.GetFrameworkCompetenciesUngrouped(frameworkId);
+ model.FrameworkCompetencies = frameworkService.GetFrameworkCompetenciesUngrouped(frameworkId, null);
model.TabNavLinks = new TabsNavViewModel(FrameworkTab.Structure, routeData);
break;
case "Comments":
@@ -747,9 +747,9 @@ public IActionResult PrintLayout(int frameworkId)
{
DetailFramework = detailFramework,
};
- model.FrameworkCompetencyGroups = frameworkService.GetFrameworkCompetencyGroups(frameworkId).ToList();
+ model.FrameworkCompetencyGroups = frameworkService.GetFrameworkCompetencyGroups(frameworkId, null).ToList();
model.CompetencyFlags = frameworkService.GetCompetencyFlagsByFrameworkId(frameworkId, null, selected: true);
- model.FrameworkCompetencies = frameworkService.GetFrameworkCompetenciesUngrouped(frameworkId);
+ model.FrameworkCompetencies = frameworkService.GetFrameworkCompetenciesUngrouped(frameworkId, null);
return View("Developer/FrameworkPrintLayout", model);
}
[ResponseCache(CacheProfileName = "Never")]
@@ -772,7 +772,7 @@ public IActionResult InsertFramework()
detailFramework.Description = SanitizerHelper.SanitizeHtmlData(detailFramework.Description);
var newFramework = frameworkService.CreateFramework(detailFramework, adminId);
TempData.Clear();
- return RedirectToAction("AddCollaborators", "Frameworks", new { actionname = "New", frameworkId = newFramework.ID });
+ return RedirectToAction("AddCollaborators", "Frameworks", new { actionName = "New", frameworkId = newFramework.ID });
}
[HttpGet]
diff --git a/DigitalLearningSolutions.Web/Controllers/FrameworksController/Review.cs b/DigitalLearningSolutions.Web/Controllers/FrameworksController/Review.cs
index e9a5304e65..8ecf417de2 100644
--- a/DigitalLearningSolutions.Web/Controllers/FrameworksController/Review.cs
+++ b/DigitalLearningSolutions.Web/Controllers/FrameworksController/Review.cs
@@ -85,7 +85,24 @@ public IActionResult SubmitFrameworkReview(int frameworkId, int reviewId, string
var framework = frameworkService.GetBaseFrameworkByFrameworkId(frameworkId, adminId);
if (framework.FrameworkReviewID == 0 || framework.FrameworkReviewID == null) return RedirectToAction("StatusCode", "LearningSolutions", new { code = 410 });
int? commentId = null;
- if (!string.IsNullOrWhiteSpace(comment)) commentId = frameworkService.InsertComment(frameworkId, adminId, comment, null);
+ if (string.IsNullOrWhiteSpace(comment))
+ {
+ ModelState.AddModelError("comment", "Please enter comment");
+ var frameworkReview = frameworkService.GetFrameworkReview(frameworkId, adminId, reviewId);
+ frameworkReview.SignedOff = signedOff;
+ var model = new SubmitReviewViewModel()
+ {
+ FrameworkId = frameworkId,
+ FrameworkName = framework.FrameworkName,
+ FrameworkReview = frameworkReview
+ };
+ return View("Developer/SubmitReview", model);
+ }
+ else
+ {
+ commentId = frameworkService.InsertComment(frameworkId, adminId, comment, null);
+ }
+
frameworkService.SubmitFrameworkReview(frameworkId, reviewId, signedOff, commentId);
frameworkNotificationService.SendReviewOutcomeNotification(reviewId, User.GetCentreIdKnownNotNull());
return RedirectToAction("ViewFramework", "Frameworks", new { frameworkId, tabname = "Structure" });
diff --git a/DigitalLearningSolutions.Web/Controllers/FrameworksController/Signposting.cs b/DigitalLearningSolutions.Web/Controllers/FrameworksController/Signposting.cs
index 9acf3bd6c9..d994dee7a9 100644
--- a/DigitalLearningSolutions.Web/Controllers/FrameworksController/Signposting.cs
+++ b/DigitalLearningSolutions.Web/Controllers/FrameworksController/Signposting.cs
@@ -107,6 +107,13 @@ public IActionResult ConfirmAddCompetencyLearningResourceSummary(CompetencyResou
{
var frameworkCompetency = frameworkService.GetFrameworkCompetencyById(model.FrameworkCompetencyId.Value);
string plainTextDescription = DisplayStringHelper.RemoveMarkup(model.Description);
+ var competencyLearningResource = competencyLearningResourcesService.GetActiveCompetencyLearningResourcesByCompetencyIdAndReferenceId(frameworkCompetency.CompetencyID, model.ReferenceId);
+ if (competencyLearningResource.Any())
+ {
+ ModelState.Clear();
+ ModelState.AddModelError("LearningResourceExists", "This learning resource is already signposted to the selected competency.");
+ return View("Developer/AddCompetencyLearningResourceSummary", model);
+ }
int competencyLearningResourceId = competencyLearningResourcesService.AddCompetencyLearningResource(model.ReferenceId, model.ResourceName, plainTextDescription, model.ResourceType, model.Link, model.SelectedCatalogue, model.Rating.Value, frameworkCompetency.CompetencyID, GetAdminId());
return RedirectToAction("StartSignpostingParametersSession", "Frameworks", new { model.FrameworkId, model.FrameworkCompetencyId, model.FrameworkCompetencyGroupId, competencyLearningResourceId });
}
diff --git a/DigitalLearningSolutions.Web/Controllers/LearningPortalController/SelfAssessment.cs b/DigitalLearningSolutions.Web/Controllers/LearningPortalController/SelfAssessment.cs
index aa3153c81d..6c071d09c8 100644
--- a/DigitalLearningSolutions.Web/Controllers/LearningPortalController/SelfAssessment.cs
+++ b/DigitalLearningSolutions.Web/Controllers/LearningPortalController/SelfAssessment.cs
@@ -67,7 +67,6 @@ public IActionResult SelfAssessment(int selfAssessmentId)
);
return RedirectToAction("StatusCode", "LearningSolutions", new { code = 403 });
}
-
selfAssessmentService.IncrementLaunchCount(selfAssessmentId, delegateUserId);
selfAssessmentService.UpdateLastAccessed(selfAssessmentId, delegateUserId);
var supervisors = selfAssessmentService.GetAllSupervisorsForSelfAssessmentId(
@@ -75,7 +74,7 @@ public IActionResult SelfAssessment(int selfAssessmentId)
delegateUserId
).ToList();
var model = new SelfAssessmentDescriptionViewModel(selfAssessment, supervisors);
-
+
return View("SelfAssessments/SelfAssessmentDescription", model);
}
@@ -119,7 +118,7 @@ public IActionResult ProcessAgreed(SelfAssessmentProcessViewModel model)
);
return RedirectToAction("StatusCode", "LearningSolutions", new { code = 403 });
}
-
+
selfAssessmentService.MarkProgressAgreed(selfAssessmentId, delegateUserId);
return RedirectToAction("SelfAssessment", new { selfAssessmentId });
@@ -675,6 +674,11 @@ public IActionResult ManageSupervisors(int selfAssessmentId)
.GetOtherSupervisorsForCandidate(selfAssessmentId, delegateUserId)
.Where(item => supervisors.All(s => !item.SupervisorAdminID.Equals(s.SupervisorAdminID)))
.ToList();
+
+ if (!assessment.NonReportable)
+ {
+ suggestedSupervisors = suggestedSupervisors.Where(s => s.SupervisorAdminUserID != delegateUserId).ToList();
+ }
}
var model = new ManageSupervisorsViewModel
@@ -731,7 +735,8 @@ public IActionResult StartAddNewSupervisor(int selfAssessmentId)
var sessionAddSupervisor = new SessionAddSupervisor()
{
SelfAssessmentID = selfAssessmentId,
- SelfAssessmentName = selfAssessment.Name
+ SelfAssessmentName = selfAssessment.Name,
+ NonReportable = selfAssessment.NonReportable
};
multiPageFormService.SetMultiPageFormData(
@@ -784,6 +789,13 @@ public IActionResult AddNewSupervisor(int selfAssessmentId,
TempData["CentreID"] = sessionAddSupervisor.CentreID;
}
+ var supervisorUserId = User.GetUserIdKnownNotNull();
+
+ if (!sessionAddSupervisor.NonReportable)
+ {
+ supervisors = supervisors.Where(s => s.AdminUserID != supervisorUserId).ToList();
+ }
+
var searchSortPaginationOptions = new SearchSortFilterAndPaginateOptions(
new SearchOptions(searchString),
new SortOptions(sortBy, sortDirection),
@@ -831,6 +843,14 @@ public IActionResult GetAllSupervisors(int selfAssessmentId)
{
supervisors = supervisors.Where(s => s.CentreID == sessionAddSupervisor.CentreID).ToList();
}
+
+ var supervisorUserId = User.GetUserIdKnownNotNull();
+
+ if (!sessionAddSupervisor.NonReportable)
+ {
+ supervisors = supervisors.Where(s => s.AdminUserID != supervisorUserId).ToList();
+ }
+
var model = new AllSupervisorsViewModel();
model.Supervisors = supervisors;
model.SupervisorAdminID = sessionAddSupervisor.SupervisorAdminId;
@@ -1904,3 +1924,4 @@ private static string RenderRazorViewToString(Controller controller, string view
}
}
}
+
diff --git a/DigitalLearningSolutions.Web/Controllers/RoleProfilesController/RoleProfiles.cs b/DigitalLearningSolutions.Web/Controllers/RoleProfilesController/RoleProfiles.cs
deleted file mode 100644
index dfcae3b8ef..0000000000
--- a/DigitalLearningSolutions.Web/Controllers/RoleProfilesController/RoleProfiles.cs
+++ /dev/null
@@ -1,306 +0,0 @@
-namespace DigitalLearningSolutions.Web.Controllers.RoleProfilesController
-{
- using DigitalLearningSolutions.Data.Models.RoleProfiles;
- using DigitalLearningSolutions.Web.ViewModels.RoleProfiles;
- using Microsoft.AspNetCore.Mvc;
- using Microsoft.Extensions.Logging;
- using DigitalLearningSolutions.Web.Extensions;
- using DigitalLearningSolutions.Data.Models.SessionData.RoleProfiles;
- using Microsoft.AspNetCore.Http;
- using System;
- using System.Collections.Generic;
- using DigitalLearningSolutions.Data.Enums;
- using DigitalLearningSolutions.Web.Attributes;
- using DigitalLearningSolutions.Web.Models.Enums;
-
- public partial class RoleProfilesController
- {
- private const string CookieName = "DLSFrameworkService";
- [Route("/RoleProfiles/View/{tabname}/{page=1:int}")]
- [SetSelectedTab(nameof(NavMenuTab.RolesProfiles))]
- public IActionResult ViewRoleProfiles(string tabname, string? searchString = null,
- string sortBy = RoleProfileSortByOptionTexts.RoleProfileName,
- string sortDirection = BaseRoleProfilesPageViewModel.AscendingText,
- int page = 1
- )
- {
- var adminId = GetAdminID();
- var isWorkforceManager = GetIsWorkforceManager();
- var isWorkforceContributor = GetIsWorkforceContributor();
- IEnumerable roleProfiles;
- if (tabname == "All")
- {
- roleProfiles = roleProfileService.GetAllRoleProfiles(adminId);
- }
- else
- {
- if (!isWorkforceContributor && !isWorkforceManager)
- {
- return RedirectToAction("ViewRoleProfiles", "RoleProfiles", new { tabname = "All" });
- }
- roleProfiles = roleProfileService.GetRoleProfilesForAdminId(adminId);
- }
- if (roleProfiles == null)
- {
- logger.LogWarning($"Attempt to display role profiles for admin {adminId} returned null.");
- return StatusCode(403);
- }
- MyRoleProfilesViewModel myRoleProfiles;
- AllRoleProfilesViewModel allRoleProfiles;
- if (tabname == "Mine")
- {
- myRoleProfiles = new MyRoleProfilesViewModel(
- roleProfiles,
- searchString,
- sortBy,
- sortDirection,
- page,
- isWorkforceManager);
- allRoleProfiles = new AllRoleProfilesViewModel(
- new List(),
- searchString,
- sortBy,
- sortDirection,
- page
- );
- }
- else
- {
- allRoleProfiles = new AllRoleProfilesViewModel(
- roleProfiles,
- searchString,
- sortBy,
- sortDirection,
- page);
- myRoleProfiles = new MyRoleProfilesViewModel(
- new List(),
- searchString,
- sortBy,
- sortDirection,
- page,
- isWorkforceManager
- );
- }
-
- var currentTab = tabname == "All" ? RoleProfilesTab.AllRoleProfiles : RoleProfilesTab.MyRoleProfiles;
- RoleProfilesViewModel? model = new RoleProfilesViewModel(
- isWorkforceManager,
- isWorkforceContributor,
- allRoleProfiles,
- myRoleProfiles,
- currentTab
- );
- return View("Index", model);
- }
- public IActionResult StartNewRoleProfileSession()
- {
- var adminId = GetAdminID();
- TempData.Clear();
- var sessionNewRoleProfile = new SessionNewRoleProfile();
- if (!Request.Cookies.ContainsKey(CookieName))
- {
- var id = Guid.NewGuid();
-
- Response.Cookies.Append(
- CookieName,
- id.ToString(),
- new CookieOptions
- {
- Expires = DateTimeOffset.UtcNow.AddDays(30)
- });
-
- sessionNewRoleProfile.Id = id;
- }
- else
- {
- if (Request.Cookies.TryGetValue(CookieName, out string idString))
- {
- sessionNewRoleProfile.Id = Guid.Parse(idString);
- }
- else
- {
- var id = Guid.NewGuid();
-
- Response.Cookies.Append(
- CookieName,
- id.ToString(),
- new CookieOptions
- {
- Expires = DateTimeOffset.UtcNow.AddDays(30)
- });
- sessionNewRoleProfile.Id = id;
- }
- }
- RoleProfileBase roleProfileBase = new RoleProfileBase()
- {
- BrandID = 6,
- OwnerAdminID = adminId,
- National = true,
- Public = true,
- PublishStatusID = 1,
- UserRole = 3
- };
- sessionNewRoleProfile.RoleProfileBase = roleProfileBase;
- TempData.Set(sessionNewRoleProfile);
- return RedirectToAction("RoleProfileName", "RoleProfiles", new { actionname = "New" });
- }
-
- [Route("/RoleProfiles/Name/{actionname}/{roleProfileId}")]
- [Route("/RoleProfiles/Name/{actionname}")]
- [SetSelectedTab(nameof(NavMenuTab.RolesProfiles))]
- public IActionResult RoleProfileName(string actionname, int roleProfileId = 0)
- {
- var adminId = GetAdminID();
- RoleProfileBase? roleProfileBase;
- if (roleProfileId > 0)
- {
- roleProfileBase = roleProfileService.GetRoleProfileBaseById(roleProfileId, adminId);
- if (roleProfileBase == null)
- {
- logger.LogWarning($"Failed to load name page for roleProfileId: {roleProfileId} adminId: {adminId}");
- return StatusCode(500);
- }
- if (roleProfileBase.UserRole < 2)
- {
- return StatusCode(403);
- }
- }
- else
- {
- SessionNewRoleProfile sessionNewRoleProfile = TempData.Peek();
- TempData.Set(sessionNewRoleProfile);
- roleProfileBase = sessionNewRoleProfile.RoleProfileBase;
- TempData.Set(sessionNewRoleProfile);
- }
- return View("Name", roleProfileBase);
- }
-
- [HttpPost]
- [Route("/RoleProfiles/Name/{actionname}/{roleProfileId}")]
- [Route("/RoleProfiles/Name/{actionname}")]
- [SetSelectedTab(nameof(NavMenuTab.RolesProfiles))]
- public IActionResult SaveProfileName(RoleProfileBase roleProfileBase, string actionname, int roleProfileId = 0)
- {
- if (!ModelState.IsValid)
- {
- ModelState.Remove(nameof(RoleProfileBase.RoleProfileName));
- ModelState.AddModelError(nameof(RoleProfileBase.RoleProfileName), "Please enter a valid role profile name (between 3 and 255 characters)");
- // do something
- return View("Name", roleProfileBase);
- }
- else
- {
- if (actionname == "New")
- {
- var sameItems = roleProfileService.GetRoleProfileByName(roleProfileBase.RoleProfileName, GetAdminID());
- if (sameItems != null)
- {
- ModelState.Remove(nameof(RoleProfileBase.RoleProfileName));
- ModelState.AddModelError(nameof(RoleProfileBase.RoleProfileName), "Another role profile exists with that name. Please choose a different name.");
- // do something
- return View("Name", roleProfileBase);
- }
- SessionNewRoleProfile sessionNewRoleProfile = TempData.Peek();
- sessionNewRoleProfile.RoleProfileBase = roleProfileBase;
- TempData.Set(sessionNewRoleProfile);
- return RedirectToAction("RoleProfileProfessionalGroup", "RoleProfiles", new { actionname });
- }
- else
- {
- var adminId = GetAdminID();
- var isUpdated = roleProfileService.UpdateRoleProfileName(roleProfileBase.ID, adminId, roleProfileBase.RoleProfileName);
- if (isUpdated)
- {
- return RedirectToAction("ViewRoleProfile", new { tabname = "Details", roleProfileId });
- }
- else
- {
- ModelState.AddModelError(nameof(RoleProfileBase.RoleProfileName), "Another role profile exists with that name. Please choose a different name.");
- // do something
- return View("Name", roleProfileBase);
- }
- }
-
- }
- }
-
- [Route("/RoleProfiles/ProfessionalGroup/{actionname}/{roleProfileId}")]
- [Route("/RoleProfiles/ProfessionalGroup/{actionname}")]
- [SetSelectedTab(nameof(NavMenuTab.RolesProfiles))]
- public IActionResult RoleProfileProfessionalGroup(string actionname, int roleProfileId = 0)
- {
- var adminId = GetAdminID();
- RoleProfileBase? roleProfileBase;
- if (roleProfileId > 0)
- {
- roleProfileBase = roleProfileService.GetRoleProfileBaseById(roleProfileId, adminId);
- if (roleProfileBase == null)
- {
- logger.LogWarning($"Failed to load Professional Group page for roleProfileId: {roleProfileId} adminId: {adminId}");
- return StatusCode(500);
- }
- if (roleProfileBase.UserRole < 2)
- {
- return StatusCode(403);
- }
- }
- else
- {
- SessionNewRoleProfile sessionNewRoleProfile = TempData.Peek();
- TempData.Set(sessionNewRoleProfile);
- roleProfileBase = sessionNewRoleProfile.RoleProfileBase;
- TempData.Set(sessionNewRoleProfile);
- }
- var professionalGroups = roleProfileService.GetNRPProfessionalGroups();
- var model = new ProfessionalGroupViewModel()
- {
- NRPProfessionalGroups = professionalGroups,
- RoleProfileBase = roleProfileBase
- };
- return View("ProfessionalGroup", model);
- }
-
- [HttpPost]
- [Route("/RoleProfiles/ProfessionalGroup/{actionname}/{roleProfileId}")]
- [Route("/RoleProfiles/ProfessionalGroup/{actionname}")]
- [SetSelectedTab(nameof(NavMenuTab.RolesProfiles))]
- public IActionResult SaveProfessionalGroup(RoleProfileBase roleProfileBase, string actionname, int roleProfileId = 0)
- {
- if (roleProfileBase.NRPProfessionalGroupID == null)
- {
- ModelState.Remove(nameof(RoleProfileBase.NRPProfessionalGroupID));
- ModelState.AddModelError(nameof(RoleProfileBase.NRPProfessionalGroupID), "Please choose a professional group" + (roleProfileId == 0 ? "or Skip this step" : "") + ".");
- // do something
- return View("Name", roleProfileBase);
- }
- if (actionname == "New")
- {
- SessionNewRoleProfile sessionNewRoleProfile = TempData.Peek();
- sessionNewRoleProfile.RoleProfileBase = roleProfileBase;
- TempData.Set(sessionNewRoleProfile);
- return RedirectToAction("RoleProfileSubGroup", "RoleProfiles", new { actionname });
- }
- else
- {
- var adminId = GetAdminID();
- var isUpdated = roleProfileService.UpdateRoleProfileProfessionalGroup(roleProfileBase.ID, adminId, roleProfileBase.NRPProfessionalGroupID);
- if (isUpdated)
- {
- return RedirectToAction("RoleProfileSubGroup", "RoleProfiles", new { actionname, roleProfileId });
- }
- else
- {
- return RedirectToAction("ViewRoleProfile", new { tabname = "Details", roleProfileId });
- }
- }
- }
-
- [Route("/RoleProfiles/SubGroup/{actionname}/{roleProfileId}")]
- [Route("/RoleProfiles/SubGroup/{actionname}")]
- [SetSelectedTab(nameof(NavMenuTab.RolesProfiles))]
- public IActionResult RoleProfileSubGroup(string actionname, int roleProfileId = 0)
- {
- return View("SubGroup");
- }
- }
-}
diff --git a/DigitalLearningSolutions.Web/Controllers/RoleProfilesController/RoleProfilesController.cs b/DigitalLearningSolutions.Web/Controllers/RoleProfilesController/RoleProfilesController.cs
deleted file mode 100644
index 9a3173289f..0000000000
--- a/DigitalLearningSolutions.Web/Controllers/RoleProfilesController/RoleProfilesController.cs
+++ /dev/null
@@ -1,54 +0,0 @@
-namespace DigitalLearningSolutions.Web.Controllers.RoleProfilesController
-{
- using DigitalLearningSolutions.Web.Helpers;
- using DigitalLearningSolutions.Web.Services;
- using Microsoft.AspNetCore.Authorization;
- using Microsoft.AspNetCore.Mvc;
- using Microsoft.Extensions.Configuration;
- using Microsoft.Extensions.Logging;
-
- [Authorize(Policy = CustomPolicies.UserFrameworksAdminOnly)]
- public partial class RoleProfilesController : Controller
- {
- private readonly IRoleProfileService roleProfileService;
- private readonly ICommonService commonService;
- private readonly IFrameworkNotificationService frameworkNotificationService;
- private readonly ILogger logger;
- private readonly IConfiguration config;
- public RoleProfilesController(
- IRoleProfileService roleProfileService,
- ICommonService commonService,
- IFrameworkNotificationService frameworkNotificationService,
- ILogger logger,
- IConfiguration config)
- {
- this.roleProfileService = roleProfileService;
- this.commonService = commonService;
- this.frameworkNotificationService = frameworkNotificationService;
- this.logger = logger;
- this.config = config;
- }
- public IActionResult Index()
- {
- return View();
- }
- private int? GetCentreId()
- {
- return User.GetCustomClaimAsInt(CustomClaimTypes.UserCentreId);
- }
- private int GetAdminID()
- {
- return User.GetCustomClaimAsRequiredInt(CustomClaimTypes.UserAdminId);
- }
- private bool GetIsWorkforceManager()
- {
- var isWorkforceManager = User.GetCustomClaimAsBool(CustomClaimTypes.IsWorkforceManager);
- return isWorkforceManager != null && (bool)isWorkforceManager;
- }
- private bool GetIsWorkforceContributor()
- {
- var isWorkforceContributor = User.GetCustomClaimAsBool(CustomClaimTypes.IsWorkforceContributor);
- return isWorkforceContributor != null && (bool)isWorkforceContributor;
- }
- }
-}
diff --git a/DigitalLearningSolutions.Web/Controllers/SupervisorController/Supervisor.cs b/DigitalLearningSolutions.Web/Controllers/SupervisorController/Supervisor.cs
index 97ab100f59..6a7e831d0b 100644
--- a/DigitalLearningSolutions.Web/Controllers/SupervisorController/Supervisor.cs
+++ b/DigitalLearningSolutions.Web/Controllers/SupervisorController/Supervisor.cs
@@ -694,9 +694,9 @@ public IActionResult StartEnrolDelegateOnProfileAssessment(int supervisorDelegat
{
TempData.Clear();
- var sessionEnrolOnRoleProfile = new SessionEnrolOnRoleProfile();
+ var sessionEnrolOnCompetencyAssessment = new SessionEnrolOnCompetencyAssessment();
multiPageFormService.SetMultiPageFormData(
- sessionEnrolOnRoleProfile,
+ sessionEnrolOnCompetencyAssessment,
MultiPageFormDataFeature.EnrolDelegateOnProfileAssessment,
TempData
);
@@ -714,12 +714,12 @@ public IActionResult StartEnrolDelegateOnProfileAssessment(int supervisorDelegat
)]
public IActionResult EnrolDelegateOnProfileAssessment(int supervisorDelegateId)
{
- var sessionEnrolOnRoleProfile = multiPageFormService.GetMultiPageFormData(
+ var sessionEnrolOnCompetencyAssessment = multiPageFormService.GetMultiPageFormData(
MultiPageFormDataFeature.EnrolDelegateOnProfileAssessment,
TempData
).GetAwaiter().GetResult();
multiPageFormService.SetMultiPageFormData(
- sessionEnrolOnRoleProfile,
+ sessionEnrolOnCompetencyAssessment,
MultiPageFormDataFeature.EnrolDelegateOnProfileAssessment,
TempData
);
@@ -727,25 +727,25 @@ public IActionResult EnrolDelegateOnProfileAssessment(int supervisorDelegateId)
var supervisorDelegate =
supervisorService.GetSupervisorDelegateDetailsById(supervisorDelegateId, GetAdminId(), 0);
- var roleProfiles = supervisorService.GetAvailableRoleProfilesForDelegate(
+ var competencyAssessments = supervisorService.GetAvailableCompetencyAssessmentsForDelegate(
(int)supervisorDelegate.DelegateUserID,
GetCentreId(),
loggedInAdmin.CategoryId
);
var model = new EnrolDelegateOnProfileAssessmentViewModel()
{
- SessionEnrolOnRoleProfile = sessionEnrolOnRoleProfile,
+ SessionEnrolOnCompetencyAssessment = sessionEnrolOnCompetencyAssessment,
SupervisorDelegateDetail = supervisorDelegate,
- RoleProfiles = roleProfiles
+ CompetencyAssessments = competencyAssessments
};
return View("EnrolDelegateOnProfileAssessment", model);
}
[HttpPost]
[Route("/Supervisor/Staff/{supervisorDelegateId}/ProfileAssessment/Enrol/Profile")]
- public IActionResult EnrolSetRoleProfile(int supervisorDelegateId, int selfAssessmentID)
+ public IActionResult EnrolSetCompetencyAssessment(int supervisorDelegateId, int selfAssessmentID)
{
- var sessionEnrolOnRoleProfile = multiPageFormService.GetMultiPageFormData(
+ var sessionEnrolOnCompetencyAssessment = multiPageFormService.GetMultiPageFormData(
MultiPageFormDataFeature.EnrolDelegateOnProfileAssessment,
TempData
).GetAwaiter().GetResult();
@@ -756,32 +756,32 @@ public IActionResult EnrolSetRoleProfile(int supervisorDelegateId, int selfAsses
{
ModelState.AddModelError("selfAssessmentId", "You must select a self assessment");
multiPageFormService.SetMultiPageFormData(
- sessionEnrolOnRoleProfile,
+ sessionEnrolOnCompetencyAssessment,
MultiPageFormDataFeature.EnrolDelegateOnProfileAssessment,
TempData
);
var supervisorDelegate =
supervisorService.GetSupervisorDelegateDetailsById(supervisorDelegateId, GetAdminId(), 0);
- var roleProfiles = supervisorService.GetAvailableRoleProfilesForDelegate(
+ var competencyAssessments = supervisorService.GetAvailableCompetencyAssessmentsForDelegate(
(int)supervisorDelegate.DelegateUserID,
GetCentreId(),
loggedInAdmin.CategoryId
);
var model = new EnrolDelegateOnProfileAssessmentViewModel()
{
- SessionEnrolOnRoleProfile = sessionEnrolOnRoleProfile,
+ SessionEnrolOnCompetencyAssessment = sessionEnrolOnCompetencyAssessment,
SupervisorDelegateDetail = supervisorDelegate,
- RoleProfiles = roleProfiles
+ CompetencyAssessments = competencyAssessments
};
return View("EnrolDelegateOnProfileAssessment", model);
}
- if (sessionEnrolOnRoleProfile.SelfAssessmentID != selfAssessmentID)
- sessionEnrolOnRoleProfile = new SessionEnrolOnRoleProfile();
+ if (sessionEnrolOnCompetencyAssessment.SelfAssessmentID != selfAssessmentID)
+ sessionEnrolOnCompetencyAssessment = new SessionEnrolOnCompetencyAssessment();
- sessionEnrolOnRoleProfile.SelfAssessmentID = selfAssessmentID;
+ sessionEnrolOnCompetencyAssessment.SelfAssessmentID = selfAssessmentID;
multiPageFormService.SetMultiPageFormData(
- sessionEnrolOnRoleProfile,
+ sessionEnrolOnCompetencyAssessment,
MultiPageFormDataFeature.EnrolDelegateOnProfileAssessment,
TempData
);
@@ -802,7 +802,7 @@ public IActionResult EnrolSetRoleProfile(int supervisorDelegateId, int selfAsses
[Route("/Supervisor/Staff/{supervisorDelegateId}/ProfileAssessment/Enrol/Confirm")]
public IActionResult ConfirmRetiringSelfAssessment(int supervisorDelegateId)
{
- var sessionEnrolOnRoleProfile = multiPageFormService.GetMultiPageFormData(
+ var sessionEnrolOnRoleProfile = multiPageFormService.GetMultiPageFormData(
MultiPageFormDataFeature.EnrolDelegateOnProfileAssessment,
TempData
).GetAwaiter().GetResult();
@@ -826,7 +826,7 @@ public IActionResult ConfirmRetiringSelfAssessment(int supervisorDelegateId)
[Route("/Supervisor/Staff/{supervisorDelegateId}/ProfileAssessment/Enrol/Confirm")]
public IActionResult ConfirmRetiringSelfAssessment(RetiringSelfAssessmentViewModel retiringSelfAssessment)
{
- var sessionEnrolOnRoleProfile = multiPageFormService.GetMultiPageFormData(
+ var sessionEnrolOnRoleProfile = multiPageFormService.GetMultiPageFormData(
MultiPageFormDataFeature.EnrolDelegateOnProfileAssessment,
TempData
).GetAwaiter().GetResult();
@@ -861,25 +861,30 @@ public IActionResult ConfirmRetiringSelfAssessment(RetiringSelfAssessmentViewMod
)]
public IActionResult EnrolDelegateCompleteBy(int supervisorDelegateId, int? day, int? month, int? year)
{
- var sessionEnrolOnRoleProfile = multiPageFormService.GetMultiPageFormData(
+ var sessionEnrolOnCompetencyAssessment = multiPageFormService.GetMultiPageFormData(
MultiPageFormDataFeature.EnrolDelegateOnProfileAssessment,
TempData
).GetAwaiter().GetResult();
+ multiPageFormService.SetMultiPageFormData(
+ sessionEnrolOnCompetencyAssessment,
+ MultiPageFormDataFeature.EnrolDelegateOnProfileAssessment,
+ TempData
+ );
- var retirementDate = selfAssessmentService.GetSelfAssessmentById((int)sessionEnrolOnRoleProfile.SelfAssessmentID).RetirementDate;
- if (SelfAssessmentHelper.CheckRetirementDate(retirementDate) && !sessionEnrolOnRoleProfile.ActionConfirmed)
+ var retirementDate = selfAssessmentService.GetSelfAssessmentById((int)sessionEnrolOnCompetencyAssessment.SelfAssessmentID).RetirementDate;
+ if (CheckRetirementDate(retirementDate) && !sessionEnrolOnCompetencyAssessment.ActionConfirmed)
{
return RedirectToAction("ConfirmRetiringSelfAssessment", "Supervisor", new { supervisorDelegateId });
}
var supervisorDelegate =
supervisorService.GetSupervisorDelegateDetailsById(supervisorDelegateId, GetAdminId(), 0);
- var roleProfile = supervisorService.GetRoleProfileById((int)sessionEnrolOnRoleProfile.SelfAssessmentID);
+ var competencyAssessment = supervisorService.GetCompetencyAssessmentById((int)sessionEnrolOnCompetencyAssessment.SelfAssessmentID);
var model = new EnrolDelegateSetCompletByDateViewModel()
{
SupervisorDelegateDetail = supervisorDelegate,
- RoleProfile = roleProfile,
- CompleteByDate = sessionEnrolOnRoleProfile.CompleteByDate,
- ActionConfirmed = sessionEnrolOnRoleProfile.ActionConfirmed
+ CompetencyAssessment = competencyAssessment,
+ CompleteByDate = sessionEnrolOnCompetencyAssessment.CompleteByDate,
+ ActionConfirmed = sessionEnrolOnCompetencyAssessment.ActionConfirmed
};
if (day != null && month != null && year != null)
{
@@ -896,22 +901,34 @@ public IActionResult EnrolDelegateSetCompleteBy(int supervisorDelegateId, int da
TempData["completeByDate"] = day;
TempData["completeByMonth"] = month;
TempData["completeByYear"] = year;
- var sessionEnrolOnRoleProfile = multiPageFormService.GetMultiPageFormData(MultiPageFormDataFeature.EnrolDelegateOnProfileAssessment, TempData).GetAwaiter().GetResult();
+ var sessionEnrolOnCompetencyAssessment = multiPageFormService.GetMultiPageFormData(MultiPageFormDataFeature.EnrolDelegateOnProfileAssessment, TempData).GetAwaiter().GetResult();
if (day != 0 | month != 0 | year != 0)
{
var validationResult = OldDateValidator.ValidateDate(day, month, year);
if (!validationResult.DateValid)
+ {
return RedirectToAction("EnrolDelegateCompleteBy", new { supervisorDelegateId, day, month, year });
+ }
+ else
+ {
+ var completeByDate = new DateTime(year, month, day);
+ sessionEnrolOnCompetencyAssessment.CompleteByDate = completeByDate;
+ multiPageFormService.SetMultiPageFormData(
+ sessionEnrolOnCompetencyAssessment,
+ MultiPageFormDataFeature.EnrolDelegateOnProfileAssessment,
+ TempData
+ );
+ }
- sessionEnrolOnRoleProfile.CompleteByDate = new DateTime(year, month, day);
+ sessionEnrolOnCompetencyAssessment.CompleteByDate = new DateTime(year, month, day);
}
else
{
- sessionEnrolOnRoleProfile.CompleteByDate = null;
+ sessionEnrolOnCompetencyAssessment.CompleteByDate = null;
}
var supervisorRoles =
- supervisorService.GetSupervisorRolesBySelfAssessmentIdForSupervisor(sessionEnrolOnRoleProfile.SelfAssessmentID.Value);
+ supervisorService.GetSupervisorRolesBySelfAssessmentIdForSupervisor(sessionEnrolOnCompetencyAssessment.SelfAssessmentID.Value);
if (supervisorRoles.Count() > 1)
{
TempData["navigatedFrom"] = "EnrolDelegateSupervisorRole";
@@ -923,9 +940,9 @@ public IActionResult EnrolDelegateSetCompleteBy(int supervisorDelegateId, int da
}
else if (supervisorRoles.Count() == 1)
{
- sessionEnrolOnRoleProfile.SelfAssessmentSupervisorRoleId = supervisorRoles.First().ID;
+ sessionEnrolOnCompetencyAssessment.SelfAssessmentSupervisorRoleId = supervisorRoles.First().ID;
multiPageFormService.SetMultiPageFormData(
- sessionEnrolOnRoleProfile,
+ sessionEnrolOnCompetencyAssessment,
MultiPageFormDataFeature.EnrolDelegateOnProfileAssessment,
TempData
);
@@ -946,21 +963,21 @@ public IActionResult EnrolDelegateSetCompleteBy(int supervisorDelegateId, int da
)]
public IActionResult EnrolDelegateSupervisorRole(int supervisorDelegateId)
{
- var sessionEnrolOnRoleProfile = multiPageFormService.GetMultiPageFormData(MultiPageFormDataFeature.EnrolDelegateOnProfileAssessment, TempData).GetAwaiter().GetResult();
+ var sessionEnrolOnCompetencyAssessment = multiPageFormService.GetMultiPageFormData(MultiPageFormDataFeature.EnrolDelegateOnProfileAssessment, TempData).GetAwaiter().GetResult();
multiPageFormService.SetMultiPageFormData(
- sessionEnrolOnRoleProfile,
+ sessionEnrolOnCompetencyAssessment,
MultiPageFormDataFeature.EnrolDelegateOnProfileAssessment,
TempData
);
var supervisorDelegate =
supervisorService.GetSupervisorDelegateDetailsById(supervisorDelegateId, GetAdminId(), 0);
- var roleProfile = supervisorService.GetRoleProfileById((int)sessionEnrolOnRoleProfile.SelfAssessmentID);
- var supervisorRoles = supervisorService.GetSupervisorRolesForSelfAssessment(sessionEnrolOnRoleProfile.SelfAssessmentID.Value);
+ var competencyAssessment = supervisorService.GetCompetencyAssessmentById((int)sessionEnrolOnCompetencyAssessment.SelfAssessmentID);
+ var supervisorRoles = supervisorService.GetSupervisorRolesForSelfAssessment(sessionEnrolOnCompetencyAssessment.SelfAssessmentID.Value);
var model = new EnrolDelegateSupervisorRoleViewModel()
{
SupervisorDelegateDetail = supervisorDelegate,
- RoleProfile = roleProfile,
- SelfAssessmentSupervisorRoleId = sessionEnrolOnRoleProfile.SelfAssessmentSupervisorRoleId,
+ CompetencyAssessment = competencyAssessment,
+ SelfAssessmentSupervisorRoleId = sessionEnrolOnCompetencyAssessment.SelfAssessmentSupervisorRoleId,
SelfAssessmentSupervisorRoles = supervisorRoles
};
ViewBag.completeByDate = TempData["completeByDate"];
@@ -977,24 +994,24 @@ public IActionResult EnrolDelegateSetSupervisorRole(
int selfAssessmentSupervisorRoleId
)
{
- var sessionEnrolOnRoleProfile = multiPageFormService.GetMultiPageFormData(MultiPageFormDataFeature.EnrolDelegateOnProfileAssessment, TempData).GetAwaiter().GetResult();
+ var sessionEnrolOnCompetencyAssessment = multiPageFormService.GetMultiPageFormData(MultiPageFormDataFeature.EnrolDelegateOnProfileAssessment, TempData).GetAwaiter().GetResult();
if (!ModelState.IsValid)
{
ModelState.ClearErrorsForAllFieldsExcept("SelfAssessmentSupervisorRoleId");
var supervisorDelegate =
supervisorService.GetSupervisorDelegateDetailsById(supervisorDelegateId, GetAdminId(), 0);
- var roleProfile = supervisorService.GetRoleProfileById((int)sessionEnrolOnRoleProfile.SelfAssessmentID);
+ var competencyAssessment = supervisorService.GetCompetencyAssessmentById((int)sessionEnrolOnCompetencyAssessment.SelfAssessmentID);
var supervisorRoles =
- supervisorService.GetSupervisorRolesForSelfAssessment(sessionEnrolOnRoleProfile.SelfAssessmentID.Value);
+ supervisorService.GetSupervisorRolesForSelfAssessment(sessionEnrolOnCompetencyAssessment.SelfAssessmentID.Value);
model.SupervisorDelegateDetail = supervisorDelegate;
- model.RoleProfile = roleProfile;
+ model.CompetencyAssessment = competencyAssessment;
model.SelfAssessmentSupervisorRoles = supervisorRoles;
return View("EnrolDelegateSupervisorRole", model);
}
- sessionEnrolOnRoleProfile.SelfAssessmentSupervisorRoleId = selfAssessmentSupervisorRoleId;
+ sessionEnrolOnCompetencyAssessment.SelfAssessmentSupervisorRoleId = selfAssessmentSupervisorRoleId;
multiPageFormService.SetMultiPageFormData(
- sessionEnrolOnRoleProfile,
+ sessionEnrolOnCompetencyAssessment,
MultiPageFormDataFeature.EnrolDelegateOnProfileAssessment,
TempData
);
@@ -1013,39 +1030,39 @@ int selfAssessmentSupervisorRoleId
)]
public IActionResult EnrolDelegateSummary(int supervisorDelegateId)
{
- var sessionEnrolOnRoleProfile = multiPageFormService.GetMultiPageFormData(MultiPageFormDataFeature.EnrolDelegateOnProfileAssessment, TempData).GetAwaiter().GetResult();
+ var sessionEnrolOnCompetencyAssessment = multiPageFormService.GetMultiPageFormData(MultiPageFormDataFeature.EnrolDelegateOnProfileAssessment, TempData).GetAwaiter().GetResult();
multiPageFormService.SetMultiPageFormData(
- sessionEnrolOnRoleProfile,
+ sessionEnrolOnCompetencyAssessment,
MultiPageFormDataFeature.EnrolDelegateOnProfileAssessment,
TempData
);
-
- var retirementDate = selfAssessmentService.GetSelfAssessmentById((int)sessionEnrolOnRoleProfile.SelfAssessmentID).RetirementDate;
- if (SelfAssessmentHelper.CheckRetirementDate(retirementDate) && !sessionEnrolOnRoleProfile.ActionConfirmed)
+
+ var retirementDate = selfAssessmentService.GetSelfAssessmentById((int)sessionEnrolOnCompetencyAssessment.SelfAssessmentID).RetirementDate;
+ if (CheckRetirementDate(retirementDate) && !sessionEnrolOnCompetencyAssessment.ActionConfirmed)
{
return RedirectToAction("ConfirmRetiringSelfAssessment", "Supervisor", new { supervisorDelegateId });
}
var supervisorDelegate =
supervisorService.GetSupervisorDelegateDetailsById(supervisorDelegateId, GetAdminId(), 0);
- var roleProfile = supervisorService.GetRoleProfileById((int)sessionEnrolOnRoleProfile.SelfAssessmentID);
- var supervisorRoleName = (sessionEnrolOnRoleProfile.SelfAssessmentSupervisorRoleId == null
+ var competencyAssessment = supervisorService.GetCompetencyAssessmentById((int)sessionEnrolOnCompetencyAssessment.SelfAssessmentID);
+ var supervisorRoleName = (sessionEnrolOnCompetencyAssessment.SelfAssessmentSupervisorRoleId == null
? "Supervisor"
: supervisorService
- .GetSupervisorRoleById(sessionEnrolOnRoleProfile.SelfAssessmentSupervisorRoleId.Value).RoleName);
- var supervisorRoleCount = (sessionEnrolOnRoleProfile.SelfAssessmentSupervisorRoleId == null
+ .GetSupervisorRoleById(sessionEnrolOnCompetencyAssessment.SelfAssessmentSupervisorRoleId.Value).RoleName);
+ var supervisorRoleCount = (sessionEnrolOnCompetencyAssessment.SelfAssessmentSupervisorRoleId == null
? 0
: supervisorService
- .GetSupervisorRolesForSelfAssessment(sessionEnrolOnRoleProfile.SelfAssessmentID.Value).Count());
- var allowSupervisorRoleSelection = (sessionEnrolOnRoleProfile.SelfAssessmentSupervisorRoleId == null
+ .GetSupervisorRolesForSelfAssessment(sessionEnrolOnCompetencyAssessment.SelfAssessmentID.Value).Count());
+ var allowSupervisorRoleSelection = (sessionEnrolOnCompetencyAssessment.SelfAssessmentSupervisorRoleId == null
? false : supervisorService
- .GetSupervisorRolesForSelfAssessment(sessionEnrolOnRoleProfile.SelfAssessmentID.Value).FirstOrDefault().AllowSupervisorRoleSelection);
+ .GetSupervisorRolesForSelfAssessment(sessionEnrolOnCompetencyAssessment.SelfAssessmentID.Value).FirstOrDefault().AllowSupervisorRoleSelection);
var model = new EnrolDelegateSummaryViewModel()
{
SupervisorDelegateDetail = supervisorDelegate,
- RoleProfile = roleProfile,
+ CompetencyAssessment = competencyAssessment,
SupervisorRoleName = supervisorRoleName,
- CompleteByDate = sessionEnrolOnRoleProfile.CompleteByDate,
+ CompleteByDate = sessionEnrolOnCompetencyAssessment.CompleteByDate,
SupervisorRoleCount = supervisorRoleCount,
AllowSupervisorRoleSelection = allowSupervisorRoleSelection
};
@@ -1053,16 +1070,16 @@ public IActionResult EnrolDelegateSummary(int supervisorDelegateId)
ViewBag.completeByMonth = TempData["completeByMonth"];
ViewBag.completeByYear = TempData["completeByYear"];
ViewBag.navigatedFrom = TempData["navigatedFrom"];
- ViewBag.actionConfirmed = sessionEnrolOnRoleProfile.ActionConfirmed;
+ ViewBag.actionConfirmed = sessionEnrolOnCompetencyAssessment.ActionConfirmed;
return View("EnrolDelegateSummary", model);
}
public IActionResult EnrolDelegateConfirm(int delegateUserId, int supervisorDelegateId)
{
- var sessionEnrolOnRoleProfile = multiPageFormService.GetMultiPageFormData(MultiPageFormDataFeature.EnrolDelegateOnProfileAssessment, TempData).GetAwaiter().GetResult();
- var selfAssessmentId = sessionEnrolOnRoleProfile.SelfAssessmentID;
- var completeByDate = sessionEnrolOnRoleProfile.CompleteByDate;
- var selfAssessmentSupervisorRoleId = sessionEnrolOnRoleProfile.SelfAssessmentSupervisorRoleId;
+ var sessionEnrolOnCompetencyAssessment = multiPageFormService.GetMultiPageFormData(MultiPageFormDataFeature.EnrolDelegateOnProfileAssessment, TempData).GetAwaiter().GetResult();
+ var selfAssessmentId = sessionEnrolOnCompetencyAssessment.SelfAssessmentID;
+ var completeByDate = sessionEnrolOnCompetencyAssessment.CompleteByDate;
+ var selfAssessmentSupervisorRoleId = sessionEnrolOnCompetencyAssessment.SelfAssessmentSupervisorRoleId;
var loggedInUserId = User.GetUserId();
var candidateAssessmentId = supervisorService.EnrolDelegateOnAssessment(
delegateUserId,
@@ -1094,7 +1111,7 @@ public IActionResult QuickAddSupervisor(int selfAssessmentId, int supervisorDele
{
var supervisorDelegate =
supervisorService.GetSupervisorDelegateDetailsById(supervisorDelegateId, GetAdminId(), 0);
- var roleProfile = supervisorService.GetRoleProfileById(selfAssessmentId);
+ var competencyAssessment = supervisorService.GetCompetencyAssessmentById(selfAssessmentId);
var supervisorRoles = supervisorService.GetSupervisorRolesBySelfAssessmentIdForSupervisor(selfAssessmentId);
if (supervisorRoles.Any() && supervisorRoles.Count() > 1)
@@ -1102,7 +1119,7 @@ public IActionResult QuickAddSupervisor(int selfAssessmentId, int supervisorDele
var model = new EnrolDelegateSupervisorRoleViewModel()
{
SupervisorDelegateDetail = supervisorDelegate,
- RoleProfile = roleProfile,
+ CompetencyAssessment = competencyAssessment,
SelfAssessmentSupervisorRoleId = null,
SelfAssessmentSupervisorRoles = supervisorRoles
};
@@ -1122,25 +1139,25 @@ public IActionResult QuickAddSupervisor(int selfAssessmentId, int supervisorDele
}
}
- var sessionEnrolOnRoleProfile = new SessionEnrolOnRoleProfile()
+ var sessionEnrolOnCompetencyAssessment = new SessionEnrolOnCompetencyAssessment()
{
SelfAssessmentID = supervisorRoles.FirstOrDefault() != null ? supervisorRoles.FirstOrDefault().SelfAssessmentID : selfAssessmentId,
SelfAssessmentSupervisorRoleId = supervisorRoles.FirstOrDefault() != null ? supervisorRoles.FirstOrDefault().ID : 0
};
multiPageFormService.SetMultiPageFormData(
- sessionEnrolOnRoleProfile,
+ sessionEnrolOnCompetencyAssessment,
MultiPageFormDataFeature.EnrolDelegateOnProfileAssessment,
TempData
);
var supervisorRoleName = supervisorRoles.FirstOrDefault() != null ? supervisorRoles.FirstOrDefault().RoleName : "";
var model = new EnrolDelegateSummaryViewModel
{
- RoleProfile = roleProfile,
+ CompetencyAssessment = competencyAssessment,
SupervisorDelegateDetail = supervisorDelegate,
SupervisorRoleName = supervisorRoleName
};
- return View("SelectDelegateSupervisorRoleSummary", new Tuple(model, sessionEnrolOnRoleProfile.SelfAssessmentSupervisorRoleId));
+ return View("SelectDelegateSupervisorRoleSummary", new Tuple(model, sessionEnrolOnCompetencyAssessment.SelfAssessmentSupervisorRoleId));
}
@@ -1150,7 +1167,7 @@ public IActionResult QuickAddSupervisor(int selfAssessmentId, int supervisorDele
[HttpPost]
public IActionResult QuickAddSupervisor(EnrolDelegateSupervisorRoleViewModel supervisorRole, int selfAssessmentId, int supervisorDelegateId, int delegateUserId)
{
- var roleProfile = supervisorService.GetRoleProfileById(selfAssessmentId);
+ var competencyAssessment = supervisorService.GetCompetencyAssessmentById(selfAssessmentId);
var supervisorDelegate =
supervisorService.GetSupervisorDelegateDetailsById(supervisorDelegateId, GetAdminId(), 0);
if (supervisorRole.SelfAssessmentSupervisorRoleId == null)
@@ -1159,7 +1176,7 @@ public IActionResult QuickAddSupervisor(EnrolDelegateSupervisorRoleViewModel sup
var model = new EnrolDelegateSupervisorRoleViewModel()
{
SupervisorDelegateDetail = supervisorDelegate,
- RoleProfile = roleProfile,
+ CompetencyAssessment = competencyAssessment,
SelfAssessmentSupervisorRoleId = null,
SelfAssessmentSupervisorRoles = supervisorRoles
};
@@ -1170,7 +1187,7 @@ public IActionResult QuickAddSupervisor(EnrolDelegateSupervisorRoleViewModel sup
var model = new EnrolDelegateSummaryViewModel
{
- RoleProfile = roleProfile,
+ CompetencyAssessment = competencyAssessment,
SupervisorDelegateDetail = supervisorDelegate,
SupervisorRoleName = supervisorRole.SelfAssessmentSupervisorRoleId == null
? "Supervisor" : supervisorService.GetSupervisorRoleById((int)supervisorRole.SelfAssessmentSupervisorRoleId).RoleName,
@@ -1189,12 +1206,12 @@ public IActionResult QuickAddSupervisorConfirm(int? selfAssessmentSupervisorRole
var supervisorDelegate = supervisorService.GetSupervisorDelegateDetailsById(supervisorDelegateId, GetAdminId(), 0);
if (!selfAssessmentSupervisorRoleId.HasValue)
{
- var roleProfile = supervisorService.GetRoleProfileById(selfAssessmentId);
+ var competencyAssessment = supervisorService.GetCompetencyAssessmentById(selfAssessmentId);
var supervisorRoles = supervisorService.GetSupervisorRolesForSelfAssessment(selfAssessmentId);
var model = new EnrolDelegateSupervisorRoleViewModel()
{
SupervisorDelegateDetail = supervisorDelegate,
- RoleProfile = roleProfile,
+ CompetencyAssessment = competencyAssessment,
SelfAssessmentSupervisorRoleId = null,
SelfAssessmentSupervisorRoles = supervisorRoles
};
@@ -1577,5 +1594,14 @@ private static string RenderRazorViewToString(Controller controller, string view
return sw.GetStringBuilder().ToString();
}
}
+ private bool CheckRetirementDate(DateTime? date)
+ {
+ if (date == null)
+ return false;
+
+ DateTime retirementOffsetDate = DateTime.Today.AddDays(14);
+ DateTime today = DateTime.Today;
+ return (date >= today && date <= retirementOffsetDate);
+ }
}
}
diff --git a/DigitalLearningSolutions.Web/Controllers/TrackingSystem/Centre/SelfAssessmentReports/SelfAssessmentReportsController.cs b/DigitalLearningSolutions.Web/Controllers/TrackingSystem/Centre/SelfAssessmentReports/SelfAssessmentReportsController.cs
index 5a59a12086..483c75ef3a 100644
--- a/DigitalLearningSolutions.Web/Controllers/TrackingSystem/Centre/SelfAssessmentReports/SelfAssessmentReportsController.cs
+++ b/DigitalLearningSolutions.Web/Controllers/TrackingSystem/Centre/SelfAssessmentReports/SelfAssessmentReportsController.cs
@@ -16,8 +16,8 @@
using Microsoft.FeatureManagement;
using Microsoft.FeatureManagement.Mvc;
using System;
+ using System.Linq;
using System.Threading.Tasks;
-
[FeatureGate(FeatureFlags.RefactoredTrackingSystem)]
[Authorize(Policy = CustomPolicies.UserCentreAdmin)]
[SetDlsSubApplication(nameof(DlsSubApplication.TrackingSystem))]
@@ -33,6 +33,7 @@ public class SelfAssessmentReportsController : Controller
private readonly string workbookName;
private readonly string viewName;
private readonly ISelfAssessmentService selfAssessmentService;
+ private readonly ICentreSelfAssessmentsService centreSelfAssessmentsService;
private readonly IFeatureManager featureManager;
public SelfAssessmentReportsController(
ISelfAssessmentReportService selfAssessmentReportService,
@@ -40,6 +41,7 @@ public SelfAssessmentReportsController(
IClockUtility clockUtility,
IConfiguration config,
ISelfAssessmentService selfAssessmentService,
+ ICentreSelfAssessmentsService centreSelfAssessmentsService,
IFeatureManager featureManager
)
{
@@ -51,6 +53,7 @@ IFeatureManager featureManager
workbookName = config.GetTableauWorkbookName();
viewName = config.GetTableauViewName();
this.selfAssessmentService = selfAssessmentService;
+ this.centreSelfAssessmentsService = centreSelfAssessmentsService;
this.featureManager = featureManager;
}
[Route("/TrackingSystem/Centre/Reports/SelfAssessments")]
@@ -59,10 +62,12 @@ public async Task IndexAsync()
var centreId = User.GetCentreId();
var adminCategoryId = User.GetAdminCategoryId();
var categoryId = this.selfAssessmentService.GetSelfAssessmentCategoryId(1);
+ var selfAssessments = centreSelfAssessmentsService.GetCentreSelfAssessments(centreId.Value);
+ var dSATreportIsPublish = selfAssessments.Any(x => x.SelfAssessmentId == 1);
var tableauFlag = await featureManager.IsEnabledAsync(FeatureFlags.TableauSelfAssessmentDashboards);
var tableauQueryOverride = string.Equals(Request.Query["tableaulink"], "true", StringComparison.OrdinalIgnoreCase);
var showTableauLink = tableauFlag || tableauQueryOverride;
- var model = new SelfAssessmentReportsViewModel(selfAssessmentReportService.GetSelfAssessmentsForReportList((int)centreId, adminCategoryId), adminCategoryId, categoryId, showTableauLink);
+ var model = new SelfAssessmentReportsViewModel(selfAssessmentReportService.GetSelfAssessmentsForReportList((int)centreId, adminCategoryId), adminCategoryId, categoryId, dSATreportIsPublish, showTableauLink);
return View(model);
}
[HttpGet]
diff --git a/DigitalLearningSolutions.Web/Controllers/TrackingSystem/Delegates/ActivityDelegatesController.cs b/DigitalLearningSolutions.Web/Controllers/TrackingSystem/Delegates/ActivityDelegatesController.cs
index f806ab1991..3352b34d74 100644
--- a/DigitalLearningSolutions.Web/Controllers/TrackingSystem/Delegates/ActivityDelegatesController.cs
+++ b/DigitalLearningSolutions.Web/Controllers/TrackingSystem/Delegates/ActivityDelegatesController.cs
@@ -244,6 +244,7 @@ public IActionResult Index(
saDelegate.LastAccessed = DateHelper.GetLocalDateTime(saDelegate.LastAccessed);
saDelegate.SubmittedDate = DateHelper.GetLocalDateTime(saDelegate.SubmittedDate);
saDelegate.RemovedDate = DateHelper.GetLocalDateTime(saDelegate.RemovedDate);
+ saDelegate.SignedOff = saDelegate.SignedOff is null ? null : DateHelper.GetLocalDateTime(saDelegate.SignedOff.Value);
}
}
diff --git a/DigitalLearningSolutions.Web/Helpers/CompetencyFilterHelper.cs b/DigitalLearningSolutions.Web/Helpers/CompetencyFilterHelper.cs
index 6dccdc57c7..413cd48339 100644
--- a/DigitalLearningSolutions.Web/Helpers/CompetencyFilterHelper.cs
+++ b/DigitalLearningSolutions.Web/Helpers/CompetencyFilterHelper.cs
@@ -54,7 +54,7 @@ bool MatchesSearch(Competency c) =>
// Define reusable filter checks
var filterChecks = new Dictionary>
{
- [SelfAssessmentCompetencyFilter.RequiresSelfAssessment] = c => c.AssessmentQuestions.Any(q => q.ResultId == null),
+ [SelfAssessmentCompetencyFilter.RequiresSelfAssessment] = c => c.AssessmentQuestions.Any(q => q.ResultId == null || q.Result == null),
[SelfAssessmentCompetencyFilter.SelfAssessed] = c => c.AssessmentQuestions.Any(q => q.ResultId != null && q.Requested == null && q.SignedOff == null),
[SelfAssessmentCompetencyFilter.ConfirmationRequested] = c => c.AssessmentQuestions.Any(q => q.Verified == null && q.Requested != null),
[SelfAssessmentCompetencyFilter.ConfirmationRejected] = c => c.AssessmentQuestions.Any(q => q.Verified.HasValue && q.SignedOff != true),
diff --git a/DigitalLearningSolutions.Web/Helpers/DisplayStringHelper.cs b/DigitalLearningSolutions.Web/Helpers/DisplayStringHelper.cs
index 5014a2d93c..9f6e6a571b 100644
--- a/DigitalLearningSolutions.Web/Helpers/DisplayStringHelper.cs
+++ b/DigitalLearningSolutions.Web/Helpers/DisplayStringHelper.cs
@@ -71,6 +71,21 @@ public static string GetPluralitySuffix(int number)
{
return number == 1 ? string.Empty : "s";
}
+ public static string PluraliseStringIfRequired(string input, int number)
+ {
+ if (number == 1)
+ {
+ return input;
+ }
+ else if (input.EndsWith("y"))
+ {
+ return input.Substring(0, input.Length - 1) + "ies";
+ }
+ else
+ {
+ return input + "s";
+ }
+ }
public static string? ReplaceNonAlphaNumericSpaceChars(string? input, string replacement)
{
diff --git a/DigitalLearningSolutions.Web/Helpers/MaxOptionalCompetenciesAttribute.cs b/DigitalLearningSolutions.Web/Helpers/MaxOptionalCompetenciesAttribute.cs
new file mode 100644
index 0000000000..3c01dad383
--- /dev/null
+++ b/DigitalLearningSolutions.Web/Helpers/MaxOptionalCompetenciesAttribute.cs
@@ -0,0 +1,38 @@
+namespace DigitalLearningSolutions.Web.Helpers;
+using System;
+using System.ComponentModel.DataAnnotations;
+
+public class MaxOptionalCountAttribute : ValidationAttribute
+{
+ private readonly string _maxProperty;
+
+ public MaxOptionalCountAttribute(string maxProperty)
+ {
+ _maxProperty = maxProperty;
+ }
+
+ protected override ValidationResult? IsValid(object? value, ValidationContext validationContext)
+ {
+ var number = value as int?;
+ if (number == null)
+ {
+ return ValidationResult.Success;
+ }
+
+ // Get the max property value (OptionalCompetenciesCount)
+ var maxProp = validationContext.ObjectType.GetProperty(_maxProperty);
+ if (maxProp == null)
+ {
+ return new ValidationResult($"Unknown property {_maxProperty}");
+ }
+
+ var maxValue = (int)maxProp.GetValue(validationContext.ObjectInstance)!;
+
+ if (number < 0 || number > maxValue)
+ {
+ return new ValidationResult($"Must be between 0 and {maxValue}.");
+ }
+
+ return ValidationResult.Success;
+ }
+}
diff --git a/DigitalLearningSolutions.Web/Helpers/ProfessionalRegistrationNumberHelper.cs b/DigitalLearningSolutions.Web/Helpers/ProfessionalRegistrationNumberHelper.cs
index 461d7ae46b..130c8d52b6 100644
--- a/DigitalLearningSolutions.Web/Helpers/ProfessionalRegistrationNumberHelper.cs
+++ b/DigitalLearningSolutions.Web/Helpers/ProfessionalRegistrationNumberHelper.cs
@@ -1,7 +1,7 @@
namespace DigitalLearningSolutions.Web.Helpers
{
- using System.Text.RegularExpressions;
using Microsoft.AspNetCore.Mvc.ModelBinding;
+ using System.Text.RegularExpressions;
public class ProfessionalRegistrationNumberHelper
{
@@ -48,13 +48,17 @@ public static void ValidateProfessionalRegistrationNumber(
);
}
- const string pattern = @"^[a-z\d-]+$";
+ const string pattern = @"^(\d{7}|[A-Za-z]{1,2}\d{6}|\d{4,8}|P?\d{5,6}|[C|P]\d{6}|[A-Za-z]?\d{5,6}|L\d{4,6}|\d{2}-[A-Za-z\d]{4,5})$";
var rg = new Regex(pattern, RegexOptions.IgnoreCase);
if (!rg.Match(prn).Success)
{
modelState.AddModelError(
"ProfessionalRegistrationNumber",
- "Invalid professional registration number format - Only alphanumeric characters (a-z, A-Z and 0-9) and hyphens (-) allowed"
+ "Invalid professional registration number format. " +
+ "Valid formats include: 7 digits (e.g., 1234567), 1–2 letters followed by 6 digits (e.g., AB123456), " +
+ "4–8 digits, an optional 'P' plus 5–6 digits, 'C' or 'P' plus 6 digits, " +
+ "an optional letter plus 5–6 digits, 'L' plus 4–6 digits, " +
+ "or 2 digits followed by a hyphen and 4–5 alphanumeric characters (e.g., 12-AB123)."
);
}
}
diff --git a/DigitalLearningSolutions.Web/Helpers/SearchHelper.cs b/DigitalLearningSolutions.Web/Helpers/SearchHelper.cs
index 381be134dd..9b3dc5acf8 100644
--- a/DigitalLearningSolutions.Web/Helpers/SearchHelper.cs
+++ b/DigitalLearningSolutions.Web/Helpers/SearchHelper.cs
@@ -6,7 +6,7 @@
using System.Linq;
using System.Text;
using DigitalLearningSolutions.Data.Helpers;
- using DigitalLearningSolutions.Data.Models.RoleProfiles;
+ using DigitalLearningSolutions.Data.Models.CompetencyAssessments;
using FuzzySharp;
using FuzzySharp.SimilarityRatio;
using FuzzySharp.SimilarityRatio.Scorer.StrategySensitive;
@@ -29,8 +29,8 @@ public static class SearchHelper
"profile", "job", "role",
};
- public static IEnumerable FilterRoleProfiles(
- IEnumerable roleProfiles,
+ public static IEnumerable FilterCompetencyAssessments(
+ IEnumerable competencyAssessments,
string? searchString,
int minMatchScore,
bool stripStopWords
@@ -38,7 +38,7 @@ bool stripStopWords
{
if (searchString == null)
{
- return roleProfiles;
+ return competencyAssessments;
}
if (stripStopWords)
@@ -46,16 +46,16 @@ bool stripStopWords
searchString = CleanSearchedWords(searchString);
}
- var query = new RoleProfile
+ var query = new CompetencyAssessment
{
- RoleProfileName = searchString.ToLower(),
+ CompetencyAssessmentName = searchString.ToLower(),
};
if (stripStopWords)
{
var results = Process.ExtractSorted(
query,
- roleProfiles,
- roleProfile => roleProfile.RoleProfileName.ToLower(),
+ competencyAssessments,
+ competencyAssessment => competencyAssessment.CompetencyAssessmentName.ToLower(),
ScorerCache.Get(),
minMatchScore
);
@@ -65,8 +65,8 @@ bool stripStopWords
{
var results = Process.ExtractSorted(
query,
- roleProfiles,
- roleProfile => roleProfile.RoleProfileName.ToLower(),
+ competencyAssessments,
+ competencyAssessment => competencyAssessment.CompetencyAssessmentName.ToLower(),
ScorerCache.Get(),
minMatchScore
);
diff --git a/DigitalLearningSolutions.Web/Helpers/SortingHelper.cs b/DigitalLearningSolutions.Web/Helpers/SortingHelper.cs
index e630a9ea9b..2a4cd083b8 100644
--- a/DigitalLearningSolutions.Web/Helpers/SortingHelper.cs
+++ b/DigitalLearningSolutions.Web/Helpers/SortingHelper.cs
@@ -3,8 +3,8 @@
using System.Collections.Generic;
using System.Linq;
using DigitalLearningSolutions.Data.Helpers;
- using DigitalLearningSolutions.Data.Models.RoleProfiles;
- using DigitalLearningSolutions.Web.ViewModels.RoleProfiles;
+ using DigitalLearningSolutions.Data.Models.CompetencyAssessments;
+ using DigitalLearningSolutions.Web.ViewModels.CompetencyAssessments;
///
/// This is the older version of the SortingHelper. For future search/sort implementations we should
@@ -13,36 +13,36 @@
///
public static class SortingHelper
{
- public static IEnumerable SortRoleProfileItems(
- IEnumerable roleProfiles,
+ public static IEnumerable SortCompetencyAssessmentItems(
+ IEnumerable competencyAssessments,
string sortBy,
string sortDirection
)
{
return sortBy switch
{
- RoleProfileSortByOptionTexts.RoleProfileName => sortDirection == BaseRoleProfilesPageViewModel.DescendingText
- ? roleProfiles.OrderByDescending(roleProfile => roleProfile.RoleProfileName)
- : roleProfiles.OrderBy(roleProfile => roleProfile.RoleProfileName),
- RoleProfileSortByOptionTexts.RoleProfileOwner => sortDirection == BaseRoleProfilesPageViewModel.DescendingText
- ? roleProfiles.OrderByDescending(roleProfile => roleProfile.Owner)
- : roleProfiles.OrderBy(roleProfile => roleProfile.Owner),
- RoleProfileSortByOptionTexts.RoleProfileCreatedDate => sortDirection == BaseRoleProfilesPageViewModel.DescendingText
- ? roleProfiles.OrderByDescending(roleProfile => roleProfile.CreatedDate)
- : roleProfiles.OrderBy(roleProfile => roleProfile.CreatedDate),
- RoleProfileSortByOptionTexts.RoleProfilePublishStatus => sortDirection == BaseRoleProfilesPageViewModel.DescendingText
- ? roleProfiles.OrderByDescending(roleProfile => roleProfile.PublishStatusID)
- : roleProfiles.OrderBy(roleProfile => roleProfile.PublishStatusID),
- RoleProfileSortByOptionTexts.RoleProfileBrand => sortDirection == BaseRoleProfilesPageViewModel.DescendingText
- ? roleProfiles.OrderByDescending(roleProfile => roleProfile.Brand)
- : roleProfiles.OrderBy(roleProfile => roleProfile.Brand),
- RoleProfileSortByOptionTexts.RoleProfileNationalRoleProfile => sortDirection == BaseRoleProfilesPageViewModel.DescendingText
- ? roleProfiles.OrderByDescending(roleProfile => roleProfile.NRPRole)
- : roleProfiles.OrderBy(roleProfile => roleProfile.NRPRole),
- RoleProfileSortByOptionTexts.RoleProfileNationalRoleGroup => sortDirection == BaseRoleProfilesPageViewModel.DescendingText
- ? roleProfiles.OrderByDescending(roleProfile => roleProfile.NRPProfessionalGroup)
- : roleProfiles.OrderBy(roleProfile => roleProfile.NRPProfessionalGroup),
- _ => roleProfiles
+ CompetencyAssessmentSortByOptionTexts.CompetencyAssessmentName => sortDirection == BaseCompetencyAssessmentsPageViewModel.DescendingText
+ ? competencyAssessments.OrderByDescending(competencyAssessment => competencyAssessment.CompetencyAssessmentName)
+ : competencyAssessments.OrderBy(competencyAssessment => competencyAssessment.CompetencyAssessmentName),
+ CompetencyAssessmentSortByOptionTexts.CompetencyAssessmentOwner => sortDirection == BaseCompetencyAssessmentsPageViewModel.DescendingText
+ ? competencyAssessments.OrderByDescending(competencyAssessment => competencyAssessment.Owner)
+ : competencyAssessments.OrderBy(competencyAssessment => competencyAssessment.Owner),
+ CompetencyAssessmentSortByOptionTexts.CompetencyAssessmentCreatedDate => sortDirection == BaseCompetencyAssessmentsPageViewModel.DescendingText
+ ? competencyAssessments.OrderByDescending(competencyAssessment => competencyAssessment.CreatedDate)
+ : competencyAssessments.OrderBy(competencyAssessment => competencyAssessment.CreatedDate),
+ CompetencyAssessmentSortByOptionTexts.CompetencyAssessmentPublishStatus => sortDirection == BaseCompetencyAssessmentsPageViewModel.DescendingText
+ ? competencyAssessments.OrderByDescending(competencyAssessment => competencyAssessment.PublishStatusID)
+ : competencyAssessments.OrderBy(competencyAssessment => competencyAssessment.PublishStatusID),
+ CompetencyAssessmentSortByOptionTexts.CompetencyAssessmentBrand => sortDirection == BaseCompetencyAssessmentsPageViewModel.DescendingText
+ ? competencyAssessments.OrderByDescending(competencyAssessment => competencyAssessment.Brand)
+ : competencyAssessments.OrderBy(competencyAssessment => competencyAssessment.Brand),
+ CompetencyAssessmentSortByOptionTexts.CompetencyAssessmentNationalCompetencyAssessment => sortDirection == BaseCompetencyAssessmentsPageViewModel.DescendingText
+ ? competencyAssessments.OrderByDescending(competencyAssessment => competencyAssessment.NRPRole)
+ : competencyAssessments.OrderBy(competencyAssessment => competencyAssessment.NRPRole),
+ CompetencyAssessmentSortByOptionTexts.CompetencyAssessmentNationalRoleGroup => sortDirection == BaseCompetencyAssessmentsPageViewModel.DescendingText
+ ? competencyAssessments.OrderByDescending(competencyAssessment => competencyAssessment.NRPProfessionalGroup)
+ : competencyAssessments.OrderBy(competencyAssessment => competencyAssessment.NRPProfessionalGroup),
+ _ => competencyAssessments
};
}
}
diff --git a/DigitalLearningSolutions.Web/Helpers/SupervisorCompetencyFilterHelper.cs b/DigitalLearningSolutions.Web/Helpers/SupervisorCompetencyFilterHelper.cs
index 58dfd9e95e..de2ddb814e 100644
--- a/DigitalLearningSolutions.Web/Helpers/SupervisorCompetencyFilterHelper.cs
+++ b/DigitalLearningSolutions.Web/Helpers/SupervisorCompetencyFilterHelper.cs
@@ -53,7 +53,7 @@ private static void ApplyResponseStatusFilters(ref IEnumerable compe
(f == (int)SelfAssessmentCompetencyFilter.ConfirmationRejected && c.AssessmentQuestions.Any(q => q.Verified.HasValue && q.SignedOff != true)) ||
(f == (int)SelfAssessmentCompetencyFilter.PendingConfirmation && c.AssessmentQuestions.Any(q => q.ResultId != null && q.Verified == null && q.Requested != null && q.UserIsVerifier == false)) ||
(f == (int)SelfAssessmentCompetencyFilter.AwaitingConfirmation && c.AssessmentQuestions.Any(q => q.Verified == null && q.Requested != null && q.UserIsVerifier == true)) ||
- (f == (int)SelfAssessmentCompetencyFilter.RequiresSelfAssessment && c.AssessmentQuestions.Any(q => q.ResultId == null)) ||
+ (f == (int)SelfAssessmentCompetencyFilter.RequiresSelfAssessment && c.AssessmentQuestions.Any(q => q.ResultId == null || q.Result == null)) ||
(f == (int)SelfAssessmentCompetencyFilter.SelfAssessed && c.AssessmentQuestions.Any(q => q.ResultId != null && q.Requested == null && q.SignedOff == null))
);
diff --git a/DigitalLearningSolutions.Web/Models/Enums/CompetencyAssessmentsTab.cs b/DigitalLearningSolutions.Web/Models/Enums/CompetencyAssessmentsTab.cs
new file mode 100644
index 0000000000..c77a640c7e
--- /dev/null
+++ b/DigitalLearningSolutions.Web/Models/Enums/CompetencyAssessmentsTab.cs
@@ -0,0 +1,39 @@
+namespace DigitalLearningSolutions.Web.Models.Enums
+{
+ using System.Collections.Generic;
+
+ public class CompetencyAssessmentsTab : BaseTabEnumeration
+ {
+ public static CompetencyAssessmentsTab MyCompetencyAssessments = new CompetencyAssessmentsTab(
+ 1,
+ nameof(MyCompetencyAssessments),
+ "CompetencyAssessments",
+ "ViewCompetencyAssessments",
+ "My Competency Assessments",
+ new Dictionary { { "tabName", "Mine" } }
+ );
+
+ public static CompetencyAssessmentsTab AllCompetencyAssessments = new CompetencyAssessmentsTab(
+ 2,
+ nameof(AllCompetencyAssessments),
+ "CompetencyAssessments",
+ "ViewCompetencyAssessments",
+ "All Competency Assessments",
+ new Dictionary { { "tabName", "All" } }
+ );
+
+ private CompetencyAssessmentsTab(
+ int id,
+ string name,
+ string controller,
+ string action,
+ string linkText,
+ Dictionary staticRouteData
+ ) : base(id, name, controller, action, linkText, staticRouteData) { }
+
+ public override IEnumerable GetAllTabs()
+ {
+ return GetAll();
+ }
+ }
+}
diff --git a/DigitalLearningSolutions.Web/Models/Enums/RoleProfilesTab.cs b/DigitalLearningSolutions.Web/Models/Enums/RoleProfilesTab.cs
deleted file mode 100644
index 3d599ce5d0..0000000000
--- a/DigitalLearningSolutions.Web/Models/Enums/RoleProfilesTab.cs
+++ /dev/null
@@ -1,39 +0,0 @@
-namespace DigitalLearningSolutions.Web.Models.Enums
-{
- using System.Collections.Generic;
-
- public class RoleProfilesTab : BaseTabEnumeration
- {
- public static RoleProfilesTab MyRoleProfiles = new RoleProfilesTab(
- 1,
- nameof(MyRoleProfiles),
- "RoleProfiles",
- "ViewRoleProfiles",
- "My Role Profiles",
- new Dictionary { { "tabName", "Mine" } }
- );
-
- public static RoleProfilesTab AllRoleProfiles = new RoleProfilesTab(
- 2,
- nameof(AllRoleProfiles),
- "RoleProfiles",
- "ViewRoleProfiles",
- "All Role Profiles",
- new Dictionary { { "tabName", "All" } }
- );
-
- private RoleProfilesTab(
- int id,
- string name,
- string controller,
- string action,
- string linkText,
- Dictionary staticRouteData
- ) : base(id, name, controller, action, linkText, staticRouteData) { }
-
- public override IEnumerable GetAllTabs()
- {
- return GetAll();
- }
- }
-}
diff --git a/DigitalLearningSolutions.Web/Models/GroupedCompetencyWithAssessmentRoleRequirements.cs b/DigitalLearningSolutions.Web/Models/GroupedCompetencyWithAssessmentRoleRequirements.cs
new file mode 100644
index 0000000000..ae4daa4e7d
--- /dev/null
+++ b/DigitalLearningSolutions.Web/Models/GroupedCompetencyWithAssessmentRoleRequirements.cs
@@ -0,0 +1,37 @@
+using System.Collections.Generic;
+
+namespace DigitalLearningSolutions.Web.Models
+{
+ public class GroupedCompetencyWithAssessmentRoleRequirements
+ {
+ public int CompetencyGroupID { get; set; } = 0;
+ public string GroupName { get; set; } = "Ungrouped";
+ public List Competencies { get; set; } = new();
+ }
+
+ public class CompetencyModel
+ {
+ public int CompetencyID { get; set; }
+ public string? Name { get; set; }
+ public string? Description { get; set; }
+ public bool? Optional { get; set; }
+ public List Questions { get; set; } = new();
+ }
+
+ public class AssessmentQuestionModel
+ {
+ public int AssessmentQuestionID { get; set; }
+ public string? Question { get; set; }
+ public string? InputTypeName { get; set; }
+ public bool Required { get; set; }
+ public List Responses { get; set; } = new();
+ }
+ public class ResponseModel
+ {
+ public int? ResponseValue { get; set; }
+ public string? ResponseLabel { get; set; }
+ public int? LevelRAG { get; set; }
+ }
+
+}
+
diff --git a/DigitalLearningSolutions.Web/Scripts/frameworks/branding.ts b/DigitalLearningSolutions.Web/Scripts/frameworks/branding.ts
index 805db86f09..06d5c7e0f0 100644
--- a/DigitalLearningSolutions.Web/Scripts/frameworks/branding.ts
+++ b/DigitalLearningSolutions.Web/Scripts/frameworks/branding.ts
@@ -10,8 +10,11 @@ function brandChanged() {
if (style === 'block') {
tb.value = '';
tb.required = true;
+ tb.setCustomValidity("");
tb.focus();
} else {
+ tb.value = "";
+ tb.setCustomValidity("");
tb.required = false;
}
}
@@ -27,8 +30,11 @@ function categoryChanged() {
if (style === 'block') {
tb.value = '';
tb.required = true;
+ tb.setCustomValidity("");
tb.focus();
} else {
+ tb.value = "";
+ tb.setCustomValidity("");
tb.required = false;
}
}
@@ -44,8 +50,41 @@ function topicChanged() {
if (style === 'block') {
tb.value = '';
tb.required = true;
+ tb.setCustomValidity("");
tb.focus();
} else {
+ tb.value = "";
+ tb.setCustomValidity("");
tb.required = false;
}
}
+const form = document.querySelector("form") as HTMLFormElement;
+const bfield = document.getElementById('brand-field');
+const cfield = document.getElementById('category-field');
+const tfield = document.getElementById('topic-field');
+function wireClearOnInput(input: HTMLInputElement) {
+ input.addEventListener("input", () => {
+ input.setCustomValidity("");
+ });
+}
+
+if (bfield) wireClearOnInput(bfield);
+if (cfield) wireClearOnInput(cfield);
+if (tfield) wireClearOnInput(tfield);
+
+form.addEventListener("submit", (e: Event) => {
+ [
+ { el: bfield, msg: "Please enter a valid brand." },
+ { el: cfield, msg: "Please enter a valid category." },
+ { el: tfield, msg: "Please enter a valid topic." }
+ ].forEach(f => {
+ if (f.el.required && !f.el.value.trim()) {
+ e.preventDefault();
+ f.el.setCustomValidity(f.msg);
+ f.el.reportValidity();
+ f.el.focus();
+ } else {
+ f.el.setCustomValidity("");
+ }
+ });
+});
diff --git a/DigitalLearningSolutions.Web/Scripts/frameworks/htmleditor.ts b/DigitalLearningSolutions.Web/Scripts/frameworks/htmleditor.ts
index 129697c0af..3952d26df3 100644
--- a/DigitalLearningSolutions.Web/Scripts/frameworks/htmleditor.ts
+++ b/DigitalLearningSolutions.Web/Scripts/frameworks/htmleditor.ts
@@ -70,14 +70,19 @@ if (jodited === false) {
const clean = DOMPurify.sanitize(editor.editor.innerHTML);
editor.editor.innerHTML = clean;
});
+
+ document.addEventListener('DOMContentLoaded', () => {
+ removeWaveErrors();
+ removeDevToolsIssues();
+ });
+
+ // ** Start* for jodit editor error (display red outline, focus on summary error text click) ****
const textarea = document.querySelector('.nhsuk-textarea.html-editor.nhsuk-input--error') as HTMLTextAreaElement | null;
if (textarea) {
const editorDiv = document.querySelector('.jodit-container.jodit.jodit_theme_default.jodit-wysiwyg_mode') as HTMLDivElement | null;
editorDiv?.classList.add('jodit-container', 'jodit', 'jodit_theme_default', 'jodit-wysiwyg_mode', 'jodit-error');
}
-
const summary = document.querySelector('.nhsuk-list.nhsuk-error-summary__list') as HTMLDivElement | null;
-
if (summary) {
summary.addEventListener('click', (e: Event) => {
if (textarea) {
@@ -96,5 +101,70 @@ if (jodited === false) {
}
});
}
+ // ** End* for jodit editor error (display red outline, focus on summary error text click) ****
+ }
+}
+
+function removeWaveErrors() {
+ const input = Array.from(document.querySelectorAll('input[tab-index="-1"]'))
+ .find((el) => el.style.width === '0px' && el.style.height === '0px'
+ && el.style.position === 'absolute' && el.style.visibility === 'hidden');
+
+ if (input) {
+ input.setAttribute('aria-label', 'Hidden input for accessibility');
+ input.setAttribute('title', 'HiddenInput');
+ }
+
+ const observer = new MutationObserver((mutations, obs) => {
+ const textarea = document.querySelector('.ace_text-input') as HTMLTextAreaElement | null;
+ if (textarea) {
+ textarea.setAttribute('aria-label', 'ace_text-input');
+ obs.disconnect();
+ }
+ });
+ observer.observe(document.body, {
+ childList: true,
+ subtree: true,
+ });
+}
+function removeDevToolsIssues() {
+ // set role = 'list' to toolbar
+ const toolbarbox = document.querySelector('.jodit-toolbar__box') as HTMLElement | null;
+ if (toolbarbox) {
+ toolbarbox.setAttribute('role', 'list');
+ }
+ // set role = 'list' to statusbar
+ const statusbar = document.querySelector('.jodit-xpath') as HTMLElement | null;
+ if (statusbar) {
+ statusbar.setAttribute('role', 'list');
}
+ document.querySelectorAll('.jodit-toolbar-button__trigger').forEach((el) => {
+ el.removeAttribute('role');
+ });
+ // observer to detect role='trigger' and remove role
+ const observer = new MutationObserver(() => {
+ document.querySelectorAll('.jodit-toolbar-button__trigger').forEach((el) => {
+ el.removeAttribute('role');
+ });
+ });
+ const target = document.querySelector('.jodit-toolbar__box');
+ if (target) {
+ observer.observe(target, { subtree: true, childList: true });
+ }
+
+ // observer to detect iframe and set title
+ const observer2 = new MutationObserver(() => {
+ const hiddenIframe = Array.from(document.querySelectorAll('iframe')).find((iframe) => {
+ const rect = iframe.getBoundingClientRect();
+ return rect.width === 0 && rect.height === 0 && (iframe.src === 'about:blank' || iframe.getAttribute('src') === 'about:blank');
+ });
+ if (hiddenIframe) {
+ hiddenIframe.setAttribute('title', 'Hidden iframe');
+ observer2.disconnect(); // Stop observing once found
+ }
+ });
+ observer2.observe(document.body, {
+ childList: true,
+ subtree: true,
+ });
}
diff --git a/DigitalLearningSolutions.Web/Scripts/frameworks/selectoptionalcompetencies.ts b/DigitalLearningSolutions.Web/Scripts/frameworks/selectoptionalcompetencies.ts
new file mode 100644
index 0000000000..3df001ea91
--- /dev/null
+++ b/DigitalLearningSolutions.Web/Scripts/frameworks/selectoptionalcompetencies.ts
@@ -0,0 +1,36 @@
+document.addEventListener('DOMContentLoaded', () => {
+ const groups = document.querySelectorAll('.nhsuk-checkboxes');
+
+ groups.forEach((group) => {
+ const groupToggle = group.querySelector('input[name="GroupIds"]');
+ if (!groupToggle) return;
+
+ // All individual competency checkboxes in the group
+ const childCheckboxes = group.querySelectorAll(
+ 'input[name="SelectedCompetencyIds"]',
+ );
+
+ const updateState = () => {
+ const isChecked = groupToggle.checked;
+
+ childCheckboxes.forEach((cb) => {
+ if (isChecked) {
+ // eslint-disable-next-line no-param-reassign
+ cb.checked = true; // force selected
+ // eslint-disable-next-line no-param-reassign
+ cb.disabled = true; // lock them
+ } else {
+ // eslint-disable-next-line no-param-reassign
+ cb.disabled = false; // re-enable when group is unchecked
+ // optional: leave cb.checked unchanged
+ }
+ });
+ };
+
+ // Run when the group checkbox changes
+ groupToggle.addEventListener('change', updateState);
+
+ // Also run at page load in case some are pre-checked server-side
+ updateState();
+ });
+});
diff --git a/DigitalLearningSolutions.Web/ServiceFilter/RedirectToErrorEmptySessionData.cs b/DigitalLearningSolutions.Web/ServiceFilter/RedirectToErrorEmptySessionData.cs
index 1a50a20ae6..ef5a5e36a0 100644
--- a/DigitalLearningSolutions.Web/ServiceFilter/RedirectToErrorEmptySessionData.cs
+++ b/DigitalLearningSolutions.Web/ServiceFilter/RedirectToErrorEmptySessionData.cs
@@ -34,7 +34,7 @@ public void OnActionExecuting(ActionExecutingContext context)
return;
}
- if (context.ActionArguments.ContainsKey("actionname") && context.ActionArguments["actionname"].ToString() == "Edit")
+ if (context.ActionArguments.ContainsKey("actionName") && context.ActionArguments["actionName"].ToString() == "Edit")
{
return;
}
diff --git a/DigitalLearningSolutions.Web/ServiceFilter/RequireProcessAgreementFilter .cs b/DigitalLearningSolutions.Web/ServiceFilter/RequireProcessAgreementFilter .cs
index 67e11614a0..9c480ebca7 100644
--- a/DigitalLearningSolutions.Web/ServiceFilter/RequireProcessAgreementFilter .cs
+++ b/DigitalLearningSolutions.Web/ServiceFilter/RequireProcessAgreementFilter .cs
@@ -49,7 +49,7 @@ public void OnActionExecuting(ActionExecutingContext context)
context.Result = new RedirectToActionResult("StatusCode", "LearningSolutions", new { code = 403 });
return;
}
-
+ if (!selfAssessment.IncludeLearnerDeclarationPrompt) return;
var actionName = context.RouteData.Values["action"]?.ToString();
if (actionName == "AgreeSelfAssessmentProcess" || actionName == "ProcessAgreed")
{
diff --git a/DigitalLearningSolutions.Web/Services/CompetencyAssessmentService.cs b/DigitalLearningSolutions.Web/Services/CompetencyAssessmentService.cs
new file mode 100644
index 0000000000..ab2bffac1b
--- /dev/null
+++ b/DigitalLearningSolutions.Web/Services/CompetencyAssessmentService.cs
@@ -0,0 +1,593 @@
+using DigitalLearningSolutions.Data.DataServices;
+using DigitalLearningSolutions.Data.Models.CompetencyAssessments;
+using DigitalLearningSolutions.Web.Helpers;
+using DigitalLearningSolutions.Web.Models;
+using DigitalLearningSolutions.Web.ViewModels.Frameworks;
+using DigitalLearningSolutions.Web.ViewModels.TrackingSystem.Centre.Reports;
+using DocumentFormat.OpenXml.Drawing.Charts;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace DigitalLearningSolutions.Web.Services
+{
+ public interface ICompetencyAssessmentService
+ {
+ //GET DATA
+ IEnumerable GetAllCompetencyAssessments(int adminId);
+
+ IEnumerable GetCompetencyAssessmentsForAdminId(int adminId);
+
+ CompetencyAssessmentBase? GetCompetencyAssessmentBaseById(int competencyAssessmentId, int adminId);
+
+ CompetencyAssessmentBase? GetCompetencyAssessmentBaseByName(string competencyAssessmentName, int adminId);
+ CompetencyAssessment? GetCompetencyAssessmentById(int competencyAssessmentId, int adminId);
+
+ IEnumerable GetNRPProfessionalGroups();
+ IEnumerable GetNRPSubGroups(int? nRPProfessionalGroupID);
+ IEnumerable GetNRPRoles(int? nRPSubGroupID);
+
+ CompetencyAssessmentTaskStatus GetCompetencyAssessmentTaskStatus(int assessmentId, int? frameworkId);
+ int[] GetLinkedFrameworkIds(int assessmentId);
+ int? GetPrimaryLinkedFrameworkId(int assessmentId);
+
+ IEnumerable GetCompetenciesForCompetencyAssessment(int competencyAssessmentId);
+ IEnumerable GetLinkedFrameworksForCompetencyAssessment(int competencyAssessmentId);
+
+ bool RemoveSelfAssessmentFramework(int assessmentId, int frameworkId, int adminId);
+
+ int[] GetLinkedFrameworkCompetencyIds(int competencyAssessmentId, int frameworkId);
+ CompetencyAssessmentFeatures? GetCompetencyAssessmentFeaturesTaskStatus(int competencyAssessmentId);
+ int? GetSelfAssessmentStructure(int competencyAssessmentId);
+ List GetGroupedCompetencyWithAssessmentRoleRequirements(int competencyAssessmentId, int? competencyId, int? assessmentQuestionId);
+ int GetCountOfAsssessmentQuestionInCompetencyAssessment(int competencyAssessmentId, int assessmentQuestionId);
+ IEnumerable GetCompetencySelfAssessmentReviews(int competencyAssessmentId);
+ SelfAssessmentReview? GetCompetencySelfAssessmentReviewById(int competencyAssessmentId, int selfAssessmentReviewId);
+ SelfAssessmentReviewOutcomeNotification? GetSelfAssessmentReviewNotification(int reviewId);
+
+ //UPDATE DATA
+ bool UpdateCompetencyAssessmentName(int competencyAssessmentId, int adminId, string competencyAssessmentName);
+ bool UpdateCompetencyRoleProfileLinks(int competencyAssessmentId, int adminId, int? professionalGroupId, int? subGroupId, int? roleId);
+ bool UpdateIntroductoryTextTaskStatus(int assessmentId, bool taskStatus);
+ bool UpdateCompetencyAssessmentDescription(int assessmentId, int adminId, string description);
+ bool UpdateCompetencyAssessmentBranding(int assessmentId, int adminId, int brandID, int categoryID);
+ bool UpdateBrandingTaskStatus(int assessmentId, bool taskStatus);
+ bool UpdateCompetencyAssessmentVocabulary(int assessmentId, int adminId, string vocabulary);
+ bool UpdateVocabularyTaskStatus(int assessmentId, bool taskStatus);
+ bool UpdateRoleProfileLinksTaskStatus(int assessmentId, bool taskStatus);
+ bool UpdateFrameworkLinksTaskStatus(int assessmentId, bool taskStatus, bool? previousStatus);
+ bool UpdateSelectCompetenciesTaskStatus(int competencyAssessmentId, bool taskStatus, bool? previousStatus);
+ bool UpdateOptionalCompetenciesTaskStatus(int assessmentId, bool taskStatus, bool? previousStatus);
+ bool UpdateRoleRequirementsTaskStatus(int assessmentId, bool taskStatus, bool? previousStatus);
+ bool UpdateWorkingGroupTaskStatus(int assessmentId, bool taskStatus, bool? previousStatus);
+ bool UpdateCompetencyAssessmentRoleRequirementsTaskStatus(int assessmentId, bool taskStatus, bool? previousStatus);
+ bool UpdateCompetencyAssessmentOptions(
+ bool includeLearnerDeclarationPrompt,
+ bool includesSignposting,
+ bool linearNavigation,
+ bool useDescriptionExpanders,
+ string? questionLabelText,
+ string? reviewerCommentsLabelText,
+ int competencyAssessmentId, int adminId);
+ bool UpdateCompetencyAssessmentOptionsTaskStatus(int assessmentId, bool taskStatus);
+ void MoveCompetencyInSelfAssessment(int competencyAssessmentId,
+ int competencyId,
+ string direction
+ );
+ void MoveCompetencyGroupInSelfAssessment(int competencyAssessmentId,
+ int groupId,
+ string direction
+ );
+ bool UpdateCompetencyAssessmentFeaturesTaskStatus(int id, bool descriptionStatus, bool providerandCategoryStatus, bool vocabularyStatus,
+ bool workingGroupStatus, bool AllframeworkCompetenciesStatus);
+ void UpdateSelfAssessmentFromFramework(int selfAssessmentId, int? frameworkId);
+ bool UpdateOptionalCompetenciesInAssessment(int selfAssessmentId, int[] groupIds, int[] selectedStructureIds);
+ void UpdateMinimumOptionalCompetencies(int selfAssessmentId, int minimumOptionalCompetecies);
+ void UpdateManageOptionalCompetenciesPrompt(int selfAssessmentId, string? manageOptionalCompetenciesPrompt);
+ bool UpdatePrimaryFrameworkCompetencies(int assessmentId, int frameworkId);
+ void UpdateRoleRequirementsFlags(int assessmentId, bool enforceRoleRequirementsForSignOff, bool includeRequirementsFilters);
+ int UpdateAssessmentQuestionRoleRequirementsForSelfAssessment(int assessmentId, int assessmentQuestionId, Dictionary responseRoleRequirements);
+ int UpdateCompetencyAssessmentQuestionRoleRequirement(int assessmentId, int competencyId, int assessmentQuestionId, Dictionary responseRoleRequirements);
+ bool UpdateSupervisorRolesTaskStatus(int competencyAssessmentId, bool taskCompleteChecked);
+ bool UpdateSelfAssessments(int competencyAssessmentId,
+ int? signoff,
+ int? confirm,
+ int? supervisorDeclarationValue,
+ string? supervisorCustomText,
+ int? leanerDeclarationValue,
+ string? leanerCustomText
+ );
+ void UpdateCompetencyAssessmentPublishStatus(int competencyAssessmentId, int status, int adminId);
+ void UpdateCompetencyAssessmentPublish(int competencyAssessmentId, int status, int adminId, bool national, bool pub);
+ void ArchiveSelfAssessmentReviewRequest(int reviewId);
+ void UpdateSelfAssessmentReview(int selfAssessmentID, int reviewId, bool signedOff, int? commentId);
+ void UpdateReviewRequestedDate(int reviewId);
+ bool UpdateCompetencyAssessmentReviewTaskStatus(int assessmentId, bool taskStatus);
+ //INSERT DATA
+ int InsertCompetencyAssessment(int adminId, int centreId, string competencyAssessmentName, int? frameworkId);
+ bool InsertSelfAssessmentFramework(int adminId, int assessmentId, int frameworkId);
+ int GetCompetencyCountByFrameworkId(int competencyAssessmentId, int frameworkId);
+ bool InsertCompetenciesIntoAssessmentFromFramework(int[] selectedCompetencyIds, int frameworkId, int competencyAssessmentId);
+ bool InsertSelfAssessmentUngroupedCompetencies(int selfAssessmentId, int? frameworkId);
+ bool InsertSelfAssessmentGroupedCompetencies(int selfAssessmentId, int? frameworkId);
+ void InsertIntoSelfAssessmentCollaboratorsFromFrameworkCollaborators(int selfAssessmentId, int? frameworkId);
+ void InsertSelfAssessmentReview(int competencyAssessmentId, int selfAssessmentCollaboratorID, bool required);
+ int InsertComment(int selfAssessmentID, int adminId, string comment, int? replyToCommentId);
+ int InsertCompetencySelfAssessmentReview(int reviewId);
+ //DELETE DATA
+ bool RemoveFrameworkCompetenciesFromAssessment(int competencyAssessmentId, int frameworkId);
+ bool RemoveCompetencyFromAssessment(int competencyAssessmentId, int competencyId);
+ bool RemoveCompetencyGroupFromAssessment(int competencyAssessmentId, int competencyGroupId);
+ IEnumerable GetCollaboratorsForCompetencyAssessmentId(int competencyAssessmentId);
+ int AddCollaboratorToCompetencyAssessment(int competencyAssessmentId, string? userEmail, bool canModify, int? centreID);
+ void RemoveCollaboratorFromCompetencyAssessment(int competencyAssessmentId, int id);
+ CompetencyAssessmentCollaboratorNotification? GetCollaboratorNotification(int id, int invitedByAdminId);
+ bool HasCompetencyWithSignpostedLearning(int competencyAssessmentId);
+ }
+ public class CompetencyAssessmentService : ICompetencyAssessmentService
+ {
+ private readonly ICompetencyAssessmentDataService competencyAssessmentDataService;
+ private readonly IFrameworkDataService frameworkDataService;
+ public CompetencyAssessmentService(ICompetencyAssessmentDataService competencyAssessmentDataService, IFrameworkDataService frameworkDataService)
+ {
+ this.competencyAssessmentDataService = competencyAssessmentDataService;
+ this.frameworkDataService = frameworkDataService;
+ }
+ public IEnumerable GetAllCompetencyAssessments(int adminId)
+ {
+ return competencyAssessmentDataService.GetAllCompetencyAssessments(adminId);
+ }
+
+ public IEnumerable GetNRPProfessionalGroups()
+ {
+ return competencyAssessmentDataService.GetNRPProfessionalGroups();
+ }
+
+ public CompetencyAssessmentBase? GetCompetencyAssessmentBaseById(int competencyAssessmentId, int adminId)
+ {
+ return competencyAssessmentDataService.GetCompetencyAssessmentBaseById(competencyAssessmentId, adminId);
+ }
+
+ public CompetencyAssessmentBase? GetCompetencyAssessmentBaseByName(string competencyAssessmentName, int adminId)
+ {
+ return competencyAssessmentDataService.GetCompetencyAssessmentBaseByName(competencyAssessmentName, adminId);
+ }
+
+ public IEnumerable GetCompetencyAssessmentsForAdminId(int adminId)
+ {
+ return competencyAssessmentDataService.GetCompetencyAssessmentsForAdminId(adminId);
+ }
+ public int InsertCompetencyAssessment(int adminId, int centreId, string competencyAssessmentName, int? frameworkId)
+ {
+ var assessmentId = competencyAssessmentDataService.InsertCompetencyAssessment(adminId, centreId, competencyAssessmentName);
+ if (assessmentId > 0 && frameworkId != null)
+ {
+ var framework = frameworkDataService.GetBrandedFrameworkByFrameworkId((int)frameworkId, adminId);
+ if (framework != null)
+ {
+ competencyAssessmentDataService.InsertSelfAssessmentFramework(adminId, assessmentId, framework.ID);
+ competencyAssessmentDataService.UpdateCompetencyAssessmentDescription(adminId, assessmentId, framework.Description);
+ competencyAssessmentDataService.UpdateCompetencyAssessmentBranding(assessmentId, adminId, (int)framework.BrandID, (int)framework.CategoryID);
+ competencyAssessmentDataService.UpdateCompetencyAssessmentVocabulary(assessmentId, adminId, framework.Vocabulary);
+ }
+ }
+ return assessmentId;
+ }
+
+ public bool UpdateCompetencyAssessmentName(int competencyAssessmentId, int adminId, string competencyAssessmentName)
+ {
+ return competencyAssessmentDataService.UpdateCompetencyAssessmentName(competencyAssessmentId, adminId, competencyAssessmentName);
+ }
+
+ public bool UpdateCompetencyRoleProfileLinks(int competencyAssessmentId, int adminId, int? professionalGroupId, int? subGroupId, int? roleId)
+ {
+ return competencyAssessmentDataService.UpdateCompetencyRoleProfileLinks(competencyAssessmentId, adminId, professionalGroupId, subGroupId, roleId);
+ }
+ public CompetencyAssessmentTaskStatus GetCompetencyAssessmentTaskStatus(int assessmentId, int? frameworkId)
+ {
+ return competencyAssessmentDataService.GetOrInsertAndReturnAssessmentTaskStatus(assessmentId, (frameworkId != null));
+ }
+ public bool UpdateCompetencyAssessmentDescription(int competencyAssessmentId, int adminId, string description)
+ {
+ return competencyAssessmentDataService.UpdateCompetencyAssessmentDescription(competencyAssessmentId, adminId, description);
+ }
+ public bool UpdateIntroductoryTextTaskStatus(int assessmentId, bool taskStatus)
+ {
+ return competencyAssessmentDataService.UpdateIntroductoryTextTaskStatus(assessmentId, taskStatus);
+ }
+
+ public CompetencyAssessment? GetCompetencyAssessmentById(int competencyAssessmentId, int adminId)
+ {
+ return competencyAssessmentDataService.GetCompetencyAssessmentById(competencyAssessmentId, adminId);
+ }
+
+ public bool UpdateCompetencyAssessmentBranding(int assessmentId, int adminId, int brandID, int categoryID)
+ {
+ return competencyAssessmentDataService.UpdateCompetencyAssessmentBranding(assessmentId, adminId, brandID, categoryID);
+ }
+
+ public bool UpdateBrandingTaskStatus(int assessmentId, bool taskStatus)
+ {
+ return competencyAssessmentDataService.UpdateBrandingTaskStatus(assessmentId, taskStatus);
+ }
+
+ bool ICompetencyAssessmentService.UpdateCompetencyAssessmentVocabulary(int assessmentId, int adminId, string vocabulary)
+ {
+ return competencyAssessmentDataService.UpdateCompetencyAssessmentVocabulary(assessmentId, adminId, vocabulary);
+ }
+
+ bool ICompetencyAssessmentService.UpdateVocabularyTaskStatus(int assessmentId, bool taskStatus)
+ {
+ return competencyAssessmentDataService.UpdateVocabularyTaskStatus(assessmentId, taskStatus);
+ }
+
+ public IEnumerable GetNRPSubGroups(int? nRPProfessionalGroupID)
+ {
+ return competencyAssessmentDataService.GetNRPSubGroups(nRPProfessionalGroupID);
+ }
+
+ public IEnumerable GetNRPRoles(int? nRPSubGroupID)
+ {
+ return competencyAssessmentDataService.GetNRPRoles(nRPSubGroupID);
+ }
+
+ public bool UpdateRoleProfileLinksTaskStatus(int assessmentId, bool taskStatus)
+ {
+ return competencyAssessmentDataService.UpdateRoleProfileLinksTaskStatus(assessmentId, taskStatus);
+ }
+
+ public int[] GetLinkedFrameworkIds(int assessmentId)
+ {
+ return competencyAssessmentDataService.GetLinkedFrameworkIds(assessmentId);
+ }
+
+ public bool InsertSelfAssessmentFramework(int adminId, int assessmentId, int frameworkId)
+ {
+ return competencyAssessmentDataService.InsertSelfAssessmentFramework(adminId, assessmentId, frameworkId);
+ }
+
+ public bool UpdateFrameworkLinksTaskStatus(int assessmentId, bool taskStatus, bool? previousStatus)
+ {
+ return competencyAssessmentDataService.UpdateFrameworkLinksTaskStatus(assessmentId, taskStatus, previousStatus);
+ }
+
+ public int? GetPrimaryLinkedFrameworkId(int assessmentId)
+ {
+ return competencyAssessmentDataService.GetPrimaryLinkedFrameworkId(assessmentId);
+ }
+
+ public bool RemoveSelfAssessmentFramework(int assessmentId, int frameworkId, int adminId)
+ {
+ return competencyAssessmentDataService.RemoveSelfAssessmentFramework(assessmentId, frameworkId, adminId);
+ }
+
+ public int GetCompetencyCountByFrameworkId(int competencyAssessmentId, int frameworkId)
+ {
+ return competencyAssessmentDataService.GetCompetencyCountByFrameworkId(competencyAssessmentId, frameworkId);
+ }
+
+ public bool RemoveFrameworkCompetenciesFromAssessment(int competencyAssessmentId, int frameworkId)
+ {
+ UpdateSelectCompetenciesTaskStatus(competencyAssessmentId, false, true);
+ UpdateOptionalCompetenciesTaskStatus(competencyAssessmentId, false, true);
+ UpdateRoleRequirementsTaskStatus(competencyAssessmentId, false, true);
+ return competencyAssessmentDataService.RemoveFrameworkCompetenciesFromAssessment(competencyAssessmentId, frameworkId);
+ }
+
+ public bool UpdateSelectCompetenciesTaskStatus(int assessmentId, bool taskStatus, bool? previousStatus)
+ {
+ return competencyAssessmentDataService.UpdateSelectCompetenciesTaskStatus(assessmentId, taskStatus, previousStatus);
+ }
+
+ public bool UpdateOptionalCompetenciesTaskStatus(int assessmentId, bool taskStatus, bool? previousStatus)
+ {
+ return competencyAssessmentDataService.UpdateOptionalCompetenciesTaskStatus(assessmentId, taskStatus, previousStatus);
+ }
+
+ public bool UpdateRoleRequirementsTaskStatus(int assessmentId, bool taskStatus, bool? previousStatus)
+ {
+ return competencyAssessmentDataService.UpdateRoleRequirementsTaskStatus(assessmentId, taskStatus, previousStatus);
+ }
+ public bool UpdateWorkingGroupTaskStatus(int assessmentId, bool taskStatus, bool? previousStatus)
+ {
+ return competencyAssessmentDataService.UpdateWorkingGroupTaskStatus(assessmentId, taskStatus, previousStatus);
+ }
+ public bool UpdateCompetencyAssessmentOptions(
+ bool includeLearnerDeclarationPrompt,
+ bool includesSignposting,
+ bool linearNavigation,
+ bool useDescriptionExpanders,
+ string? questionLabelText,
+ string? reviewerCommentsLabelText,
+ int competencyAssessmentId, int adminId)
+ {
+ return competencyAssessmentDataService.UpdateCompetencyAssessmentOptions(
+ includeLearnerDeclarationPrompt,
+ includesSignposting,
+ linearNavigation,
+ useDescriptionExpanders,
+ questionLabelText,
+ reviewerCommentsLabelText,
+ competencyAssessmentId, adminId);
+ }
+ public IEnumerable GetCompetenciesForCompetencyAssessment(int competencyAssessmentId)
+ {
+ return competencyAssessmentDataService.GetCompetenciesForCompetencyAssessment(competencyAssessmentId);
+ }
+
+ public IEnumerable GetLinkedFrameworksForCompetencyAssessment(int competencyAssessmentId)
+ {
+ return competencyAssessmentDataService.GetLinkedFrameworksForCompetencyAssessment(competencyAssessmentId);
+ }
+
+ public int[] GetLinkedFrameworkCompetencyIds(int competencyAssessmentId, int frameworkId)
+ {
+ return competencyAssessmentDataService.GetLinkedFrameworkCompetencyIds(competencyAssessmentId, frameworkId);
+ }
+
+ public bool InsertCompetenciesIntoAssessmentFromFramework(int[] selectedCompetencyIds, int frameworkId, int competencyAssessmentId)
+ {
+ return competencyAssessmentDataService.InsertCompetenciesIntoAssessmentFromFramework(selectedCompetencyIds, frameworkId, competencyAssessmentId);
+ }
+
+ public bool RemoveCompetencyFromAssessment(int competencyAssessmentId, int competencyId)
+ {
+ return competencyAssessmentDataService.RemoveCompetencyFromAssessment(competencyAssessmentId, competencyId);
+ }
+ public bool RemoveCompetencyGroupFromAssessment(int competencyAssessmentId, int competencyGroupId)
+ {
+ return competencyAssessmentDataService.RemoveCompetencyGroupFromAssessment(competencyAssessmentId, competencyGroupId);
+ }
+
+ public void MoveCompetencyInSelfAssessment(int competencyAssessmentId, int competencyId, string direction)
+ {
+ competencyAssessmentDataService.MoveCompetencyInSelfAssessment(competencyAssessmentId, competencyId, direction);
+ }
+
+ public void MoveCompetencyGroupInSelfAssessment(int competencyAssessmentId, int groupId, string direction)
+ {
+ competencyAssessmentDataService.MoveCompetencyGroupInSelfAssessment(competencyAssessmentId, groupId, direction);
+ }
+ public bool UpdateCompetencyAssessmentFeaturesTaskStatus(int id, bool descriptionStatus, bool providerandCategoryStatus, bool vocabularyStatus,
+ bool workingGroupStatus, bool AllframeworkCompetenciesStatus)
+ {
+ return competencyAssessmentDataService.UpdateCompetencyAssessmentFeaturesTaskStatus(id, descriptionStatus, providerandCategoryStatus, vocabularyStatus,
+ workingGroupStatus, AllframeworkCompetenciesStatus);
+ }
+ public bool UpdateCompetencyAssessmentRoleRequirementsTaskStatus(int assessmentId, bool taskStatus, bool? previousStatus)
+ {
+ return competencyAssessmentDataService.UpdateCompetencyAssessmentRoleRequirementsTaskStatus(assessmentId, taskStatus);
+ }
+ public CompetencyAssessmentFeatures? GetCompetencyAssessmentFeaturesTaskStatus(int competencyAssessmentId)
+ {
+ return competencyAssessmentDataService.GetCompetencyAssessmentFeaturesTaskStatus(competencyAssessmentId);
+ }
+ public bool InsertSelfAssessmentGroupedCompetencies(int selfAssessmentId, int? frameworkId)
+ {
+ return competencyAssessmentDataService.InsertSelfAssessmentGroupedCompetencies(selfAssessmentId, frameworkId);
+ }
+ public bool InsertSelfAssessmentUngroupedCompetencies(int selfAssessmentId, int? frameworkId)
+ {
+ return competencyAssessmentDataService.InsertSelfAssessmentUngroupedCompetencies(selfAssessmentId, frameworkId);
+ }
+ public void UpdateSelfAssessmentFromFramework(int selfAssessmentId, int? frameworkId)
+ {
+ competencyAssessmentDataService.UpdateSelfAssessmentFromFramework(selfAssessmentId, frameworkId);
+ }
+ public bool UpdatePrimaryFrameworkCompetencies(int assessmentId, int frameworkId)
+ {
+ return competencyAssessmentDataService.UpdatePrimaryFrameworkCompetencies(assessmentId, frameworkId);
+ }
+
+ public int? GetSelfAssessmentStructure(int competencyAssessmentId)
+ {
+ return competencyAssessmentDataService.GetSelfAssessmentStructure(competencyAssessmentId);
+ }
+
+ public bool UpdateOptionalCompetenciesInAssessment(int selfAssessmentId, int[] groupIds, int[] selectedStructureIds)
+ {
+ return competencyAssessmentDataService.UpdateOptionalCompetenciesInAssessment(selfAssessmentId, groupIds, selectedStructureIds);
+ }
+
+ public void UpdateMinimumOptionalCompetencies(int selfAssessmentId, int minimumOptionalCompetecies)
+ {
+ competencyAssessmentDataService.UpdateMinimumOptionalCompetencies(selfAssessmentId, minimumOptionalCompetecies);
+ }
+ public void UpdateManageOptionalCompetenciesPrompt(int selfAssessmentId, string? manageOptionalCompetenciesPrompt)
+ {
+ manageOptionalCompetenciesPrompt = SanitizerHelper.SanitizeHtmlData(manageOptionalCompetenciesPrompt);
+ if (StringHelper.StripHtmlTags(manageOptionalCompetenciesPrompt) == "")
+ {
+ manageOptionalCompetenciesPrompt = null;
+ }
+ competencyAssessmentDataService.UpdateManageOptionalCompetenciesPrompt(selfAssessmentId, manageOptionalCompetenciesPrompt);
+ }
+ public IEnumerable GetCollaboratorsForCompetencyAssessmentId(int competencyAssessmentId)
+ {
+ return competencyAssessmentDataService.GetCollaboratorsForCompetencyAssessmentId(competencyAssessmentId);
+ }
+ public int AddCollaboratorToCompetencyAssessment(int competencyAssessmentId, string? userEmail, bool canModify, int? centreID)
+ {
+ return competencyAssessmentDataService.AddCollaboratorToCompetencyAssessment(competencyAssessmentId, userEmail, canModify, centreID);
+ }
+ public void RemoveCollaboratorFromCompetencyAssessment(int competencyAssessmentId, int id)
+ {
+ competencyAssessmentDataService.RemoveCollaboratorFromCompetencyAssessment(competencyAssessmentId, id);
+ }
+ public CompetencyAssessmentCollaboratorNotification? GetCollaboratorNotification(int id, int invitedByAdminId)
+ {
+ return competencyAssessmentDataService.GetCollaboratorNotification(id, invitedByAdminId);
+
+ }
+ public bool HasCompetencyWithSignpostedLearning(int competencyAssessmentId)
+ {
+ return competencyAssessmentDataService.HasCompetencyWithSignpostedLearning(competencyAssessmentId);
+ }
+ public bool UpdateCompetencyAssessmentOptionsTaskStatus(int assessmentId, bool taskStatus)
+ {
+ return competencyAssessmentDataService.UpdateCompetencyAssessmentOptionsTaskStatus(assessmentId, taskStatus);
+ }
+
+ public List GetGroupedCompetencyWithAssessmentRoleRequirements(int competencyAssessmentId, int? competencyId, int? assessmentQuestionId)
+ {
+ var competencyWithAssessmentQuestionRoleRequirements = competencyAssessmentDataService.GetCompetencyWithAssessmentQuestionRoleRequirements(competencyAssessmentId, competencyId, assessmentQuestionId).ToList();
+ return [.. competencyWithAssessmentQuestionRoleRequirements
+ .GroupBy(x => new
+ {
+ x.CompetencyGroupID,
+ x.GroupName
+ })
+ .Select(group => new GroupedCompetencyWithAssessmentRoleRequirements
+ {
+ CompetencyGroupID = group.Key.CompetencyGroupID ?? 0,
+ GroupName = group.Key.GroupName ?? "Ungrouped",
+
+ Competencies = [.. group
+ .GroupBy(c => new
+ {
+ c.CompetencyID,
+ c.Competency,
+ c.CompetencyDescription,
+ c.Optional
+ })
+ .Select(comp => new CompetencyModel
+ {
+ CompetencyID = comp.Key.CompetencyID,
+ Name = comp.Key.Competency,
+ Description = comp.Key.CompetencyDescription,
+ Optional = comp.Key.Optional,
+
+ Questions = [.. comp
+ .GroupBy(q => new
+ {
+ q.AssessmentQuestionID,
+ q.Question,
+ q.InputTypeName,
+ q.Required
+ })
+ .Select(q => new AssessmentQuestionModel
+ {
+ AssessmentQuestionID = q.Key.AssessmentQuestionID,
+ Question = q.Key.Question,
+ InputTypeName = q.Key.InputTypeName,
+ Required = q.Key.Required,
+ Responses = [.. q
+ .Where(r => r.ResponseValue.HasValue)
+ .Select(r => new ResponseModel
+ {
+ ResponseValue = r.ResponseValue,
+ ResponseLabel = r.Response,
+ LevelRAG = r.LevelRAG
+ })
+ .DistinctBy(r => r.ResponseValue)]
+ })]
+ })]
+ })];
+ }
+
+ public void UpdateRoleRequirementsFlags(int assessmentId, bool enforceRoleRequirementsForSignOff, bool includeRequirementsFilters)
+ {
+ competencyAssessmentDataService.UpdateRoleRequirementsFlags(assessmentId, enforceRoleRequirementsForSignOff, includeRequirementsFilters);
+ }
+
+ public int GetCountOfAsssessmentQuestionInCompetencyAssessment(int competencyAssessmentId, int assessmentQuestionId)
+ {
+ return competencyAssessmentDataService.GetCountOfAsssessmentQuestionInCompetencyAssessment(competencyAssessmentId, assessmentQuestionId);
+ }
+
+ public int UpdateAssessmentQuestionRoleRequirementsForSelfAssessment(int assessmentId, int assessmentQuestionId, Dictionary responseRoleRequirements)
+ {
+ int rowCount = 0;
+ foreach (var responseRoleRequirement in responseRoleRequirements)
+ {
+ competencyAssessmentDataService.DeleteCompetencyAssessmentQuestionRoleRequirement(assessmentId, null, assessmentQuestionId, responseRoleRequirement.Key);
+ if (responseRoleRequirement.Value != null)
+ {
+ rowCount += competencyAssessmentDataService.InsertAssessmentQuestionRoleRequirementForSelfAssessment(assessmentId, assessmentQuestionId, responseRoleRequirement.Key, responseRoleRequirement.Value);
+ }
+ }
+ return rowCount;
+ }
+
+ public int UpdateCompetencyAssessmentQuestionRoleRequirement(int assessmentId, int competencyId, int assessmentQuestionId, Dictionary responseRoleRequirements)
+ {
+ int rowCount = 0;
+ foreach (var responseRoleRequirement in responseRoleRequirements)
+ {
+ competencyAssessmentDataService.DeleteCompetencyAssessmentQuestionRoleRequirement(assessmentId, competencyId, assessmentQuestionId, responseRoleRequirement.Key);
+ if (responseRoleRequirement.Value != null)
+ {
+ rowCount += competencyAssessmentDataService.InsertCompetencyAssessmentQuestionRoleRequirement(assessmentId, competencyId, assessmentQuestionId, responseRoleRequirement.Key, responseRoleRequirement.Value);
+ }
+ }
+ return rowCount;
+ }
+ public void InsertIntoSelfAssessmentCollaboratorsFromFrameworkCollaborators(int selfAssessmentId, int? frameworkId)
+ {
+ competencyAssessmentDataService.InsertIntoSelfAssessmentCollaboratorsFromFrameworkCollaborators(selfAssessmentId, frameworkId);
+ }
+ public bool UpdateSupervisorRolesTaskStatus(int competencyAssessmentId, bool taskCompleteChecked)
+ {
+ return competencyAssessmentDataService.UpdateSupervisorRolesTaskStatus(competencyAssessmentId, taskCompleteChecked);
+ }
+ public bool UpdateSelfAssessments(int competencyAssessmentId,
+ int? signoff,
+ int? confirm,
+ int? supervisorDeclarationValue,
+ string? supervisorCustomText,
+ int? leanerDeclarationValue,
+ string? leanerCustomText
+ )
+ {
+ return competencyAssessmentDataService.UpdateSelfAssessments(competencyAssessmentId, signoff, confirm,
+ supervisorDeclarationValue, supervisorCustomText, leanerDeclarationValue, leanerCustomText);
+ }
+ public void UpdateCompetencyAssessmentPublishStatus(int competencyAssessmentId, int status, int adminId)
+ {
+ competencyAssessmentDataService.UpdateCompetencyAssessmentPublishStatus(competencyAssessmentId, status, adminId);
+ }
+ public void InsertSelfAssessmentReview(int competencyAssessmentId, int selfAssessmentCollaboratorID, bool required)
+ {
+ competencyAssessmentDataService.InsertSelfAssessmentReview(competencyAssessmentId, selfAssessmentCollaboratorID, required);
+ }
+ public void UpdateCompetencyAssessmentPublish(int competencyAssessmentId, int status, int adminId, bool national, bool pub)
+ {
+ competencyAssessmentDataService.UpdateCompetencyAssessmentPublish(competencyAssessmentId, status, adminId, national, pub);
+ }
+ public IEnumerable GetCompetencySelfAssessmentReviews(int competencyAssessmentId)
+ {
+ return competencyAssessmentDataService.GetCompetencySelfAssessmentReviews(competencyAssessmentId);
+ }
+ public void ArchiveSelfAssessmentReviewRequest(int reviewId)
+ {
+ competencyAssessmentDataService.ArchiveSelfAssessmentReviewRequest(reviewId);
+ }
+ public void UpdateSelfAssessmentReview(int selfAssessmentID, int reviewId, bool signedOff, int? commentId)
+ {
+ competencyAssessmentDataService.UpdateSelfAssessmentReview(selfAssessmentID, reviewId, signedOff, commentId);
+ }
+ public int InsertComment(int selfAssessmentID, int adminId, string comment, int? replyToCommentId)
+ {
+ return competencyAssessmentDataService.InsertComment(selfAssessmentID, adminId, comment, replyToCommentId);
+ }
+ public SelfAssessmentReview? GetCompetencySelfAssessmentReviewById(int competencyAssessmentId, int selfAssessmentReviewId)
+ {
+ return competencyAssessmentDataService.GetCompetencySelfAssessmentReviewById(competencyAssessmentId, selfAssessmentReviewId);
+ }
+ public SelfAssessmentReviewOutcomeNotification? GetSelfAssessmentReviewNotification(int reviewId)
+ {
+ return competencyAssessmentDataService.GetSelfAssessmentReviewNotification(reviewId);
+ }
+ public void UpdateReviewRequestedDate(int reviewId)
+ {
+ competencyAssessmentDataService.UpdateReviewRequestedDate(reviewId);
+ }
+ public int InsertCompetencySelfAssessmentReview(int reviewId)
+ {
+ return competencyAssessmentDataService.InsertCompetencySelfAssessmentReview(reviewId);
+ }
+ public bool UpdateCompetencyAssessmentReviewTaskStatus(int assessmentId, bool taskStatus)
+ {
+ return competencyAssessmentDataService.UpdateCompetencyAssessmentReviewTaskStatus(assessmentId, taskStatus);
+ }
+ }
+}
diff --git a/DigitalLearningSolutions.Web/Services/CompetencyLearningResourcesService.cs b/DigitalLearningSolutions.Web/Services/CompetencyLearningResourcesService.cs
index 3f9a8b295c..525d3e87e0 100644
--- a/DigitalLearningSolutions.Web/Services/CompetencyLearningResourcesService.cs
+++ b/DigitalLearningSolutions.Web/Services/CompetencyLearningResourcesService.cs
@@ -13,6 +13,7 @@ public interface ICompetencyLearningResourcesService
IEnumerable GetCompetencyResourceAssessmentQuestionParameters(IEnumerable competencyLearningResourceIds);
int AddCompetencyLearningResource(int resourceRefID, string originalResourceName, string description, string resourceType, string link, string catalogue, decimal rating, int competencyID, int adminId);
+ IEnumerable GetActiveCompetencyLearningResourcesByCompetencyIdAndReferenceId(int competencyId, int referenceId);
}
public class CompetencyLearningResourcesService : ICompetencyLearningResourcesService
{
@@ -40,5 +41,9 @@ public IEnumerable GetCompetencyR
{
return competencyLearningResourcesDataService.GetCompetencyResourceAssessmentQuestionParameters(competencyLearningResourceIds);
}
+ public IEnumerable GetActiveCompetencyLearningResourcesByCompetencyIdAndReferenceId(int competencyId, int referenceId)
+ {
+ return competencyLearningResourcesDataService.GetActiveCompetencyLearningResourcesByCompetencyIdAndReferenceId(competencyId, referenceId);
+ }
}
}
diff --git a/DigitalLearningSolutions.Web/Services/EnrolService.cs b/DigitalLearningSolutions.Web/Services/EnrolService.cs
index 9da38c11a9..d2d20c0584 100644
--- a/DigitalLearningSolutions.Web/Services/EnrolService.cs
+++ b/DigitalLearningSolutions.Web/Services/EnrolService.cs
@@ -43,7 +43,8 @@ int EnrolOnActivitySelfAssessment(
DateTime? completeByDate,
int delegateUserId,
int centreId,
- int? enrolledByAdminId
+ int? enrolledByAdminId,
+ bool isNonReportable = false
);
}
public class EnrolService : IEnrolService
@@ -184,9 +185,9 @@ by the system because a previous course completion has expired.
return new Email(EnrolEmailSubject, body, emailAddress);
}
- public int EnrolOnActivitySelfAssessment(int selfAssessmentId, int candidateId, int supervisorId, string adminEmail, int selfAssessmentSupervisorRoleId, DateTime? completeByDate, int delegateUserId, int centreId, int? enrolledByAdminId)
+ public int EnrolOnActivitySelfAssessment(int selfAssessmentId, int candidateId, int supervisorId, string adminEmail, int selfAssessmentSupervisorRoleId, DateTime? completeByDate, int delegateUserId, int centreId, int? enrolledByAdminId, bool isNonReportable = false)
{
- return courseDataService.EnrolOnActivitySelfAssessment(selfAssessmentId, candidateId, supervisorId, adminEmail, selfAssessmentSupervisorRoleId, completeByDate, delegateUserId, centreId, enrolledByAdminId);
+ return courseDataService.EnrolOnActivitySelfAssessment(selfAssessmentId, candidateId, supervisorId, adminEmail, selfAssessmentSupervisorRoleId, completeByDate, delegateUserId, centreId, enrolledByAdminId, isNonReportable);
}
}
}
diff --git a/DigitalLearningSolutions.Web/Services/FrameworkNotificationService.cs b/DigitalLearningSolutions.Web/Services/FrameworkNotificationService.cs
index 59cbfec150..d63447a265 100644
--- a/DigitalLearningSolutions.Web/Services/FrameworkNotificationService.cs
+++ b/DigitalLearningSolutions.Web/Services/FrameworkNotificationService.cs
@@ -25,6 +25,8 @@ public interface IFrameworkNotificationService
void SendSignOffRequest(int candidateAssessmentSupervisorId, int selfAssessmentID, int delegateUserId, int centreId);
void SendProfileAssessmentSignedOff(int supervisorDelegateId, int candidateAssessmentId, string? supervisorComments, bool signedOff, int adminId, int centreId);
void SendSupervisorDelegateReminder(int supervisorDelegateId, int adminId, int centreId);
+ void SendReviewRequestForCompetencyAssessment(int id, int invitedByAdminId, bool required, bool reminder, int centreId);
+ void SendCompetencyAssessmentsReviewOutcomeNotification(int reviewId, int centreId);
}
public class FrameworkNotificationService : IFrameworkNotificationService
@@ -33,7 +35,7 @@ public class FrameworkNotificationService : IFrameworkNotificationService
private readonly IConfigDataService configDataService;
private readonly IEmailService emailService;
private readonly IFrameworkService frameworkService;
- private readonly IRoleProfileService roleProfileService;
+ private readonly ICompetencyAssessmentService competencyAssessmentService;
private readonly ISupervisorService supervisorService;
private readonly ISelfAssessmentDataService selfAssessmentDataService;
private readonly ICentresDataService centresDataService;
@@ -41,7 +43,7 @@ public FrameworkNotificationService(
IFrameworkService frameworkService,
IConfigDataService configDataService,
IEmailService emailService,
- IRoleProfileService roleProfileService,
+ ICompetencyAssessmentService competencyAssessmentService,
ISupervisorService supervisorService,
ISelfAssessmentDataService selfAssessmentDataService,
ICentresDataService centresDataService
@@ -50,7 +52,7 @@ ICentresDataService centresDataService
this.frameworkService = frameworkService;
this.configDataService = configDataService;
this.emailService = emailService;
- this.roleProfileService = roleProfileService;
+ this.competencyAssessmentService = competencyAssessmentService;
this.supervisorService = supervisorService;
this.selfAssessmentDataService = selfAssessmentDataService;
this.centresDataService = centresDataService;
@@ -140,12 +142,38 @@ public void SendReviewRequest(int id, int invitedByAdminId, bool required, bool
};
emailService.SendEmail(new Email(emailSubjectLine, builder, collaboratorNotification.UserEmail, collaboratorNotification.InvitedByEmail));
}
+ public void SendReviewRequestForCompetencyAssessment(int id, int invitedByAdminId, bool required, bool reminder, int centreId)
+ {
+ string centreName = GetCentreName(centreId);
+ var collaboratorNotification = competencyAssessmentService.GetCollaboratorNotification(id, invitedByAdminId);
+ if (collaboratorNotification == null)
+ {
+ throw new NotificationDataException($"No record found when trying to fetch collaboratorNotification Data. id: {id}, invitedByAdminId: {invitedByAdminId}");
+ }
+ var competencyAssessmentUrl = GetCompetencyAssessmentUrl(collaboratorNotification.SelfAssessmentID, "Review");
+ string emailSubjectLine = (reminder ? " REMINDER: " : "") + "Competency Assessment Review Request - Digital Learning Solutions";
+ string signOffRequired = required ? "You are required to sign-off this competency assessment before it can be published." : "You are not required to sign-off this competency assessment before it is published.";
+ var builder = new BodyBuilder
+ {
+ TextBody = $@"Dear colleague,
+ You have been requested to review the competency assessment, {collaboratorNotification?.CompetencyAssessmentName}, by {collaboratorNotification?.InvitedByName} ({collaboratorNotification?.InvitedByEmail}) ({centreName}).
+ To review the competency assessment, visit this url: {competencyAssessmentUrl}. Click the Review competency assessment button to submit your review and, if appropriate, sign-off the competency assessment. {signOffRequired}. You will need to be registered on the Digital Learning Solutions platform to review the competency assessment.",
+ HtmlBody = $@"Dear colleague,
You have been requested to review the competency assessment, {collaboratorNotification?.CompetencyAssessmentName}, by {collaboratorNotification?.InvitedByName} ({centreName}).
Click here to review the competency assessment. Click the Review competency assessment button to submit your review and, if appropriate, sign-off the competency assessment.
{signOffRequired}
You will need to be registered on the Digital Learning Solutions platform to view the competency assessment.
"
+ };
+ emailService.SendEmail(new Email(emailSubjectLine, builder, collaboratorNotification.UserEmail, collaboratorNotification.InvitedByEmail));
+ }
public string GetFrameworkUrl(int frameworkId, string tab)
{
var frameworkUrl = GetDLSUriBuilder();
frameworkUrl.Path += $"Framework/{frameworkId}/{tab}/";
return frameworkUrl.Uri.ToString();
}
+ public string GetCompetencyAssessmentUrl(int selfAssessmentID, string actionName)
+ {
+ var competencyAssessmentUrl = GetDLSUriBuilder();
+ competencyAssessmentUrl.Path += $"CompetencyAssessments/{selfAssessmentID}/{actionName}";
+ return competencyAssessmentUrl.Uri.ToString();
+ }
public string GetCurrentActivitiesUrl()
{
var dlsUrlBuilder = GetDLSUriBuilder();
@@ -203,7 +231,38 @@ public void SendReviewOutcomeNotification(int reviewId, int centreId)
};
emailService.SendEmail(new Email(emailSubjectLine, builder, outcomeNotification.OwnerEmail, outcomeNotification.UserEmail));
}
-
+ public void SendCompetencyAssessmentsReviewOutcomeNotification(int reviewId, int centreId)
+ {
+ string centreName = GetCentreName(centreId);
+ var outcomeNotification = competencyAssessmentService.GetSelfAssessmentReviewNotification(reviewId);
+ if (outcomeNotification == null)
+ {
+ throw new NotificationDataException($"No record found when trying to fetch review outcome Data. reviewId: {reviewId}");
+ }
+ var competencyAssessmentUrl = GetCompetencyAssessmentUrl(outcomeNotification.SelfAssessmentID, "PublishReview");
+ string emailSubjectLine = $"Competency Assessment Review Outcome - {(outcomeNotification.SignedOff ? "Approved" : "Rejected")} - Digital Learning Solutions";
+ string approvalStatus = outcomeNotification.ReviewerFirstName + (outcomeNotification.SignedOff ? " approved the competency assessment for publishing." : " did not approve the competency assessment for publishing.");
+ string commentsText = outcomeNotification.ReviewerFirstName + (outcomeNotification.Comment != null ? " left the following review comment: " + outcomeNotification.Comment : " did not leave a review comment.");
+ string commentsHtml = "" + outcomeNotification.ReviewerFirstName + (outcomeNotification.Comment != null ? " left the following review comment:
" + outcomeNotification.Comment + "
" : " did not leave a review comment.");
+ string reviewerFullName = $"{outcomeNotification.ReviewerFirstName} {outcomeNotification.ReviewerLastName} {(outcomeNotification.ReviewerActive == true ? "" : " (inactive)")}";
+ var builder = new BodyBuilder
+ {
+ TextBody = $@"Dear {outcomeNotification.OwnerFirstName},
+ Your competency assessment, {outcomeNotification.SelfAssessmentName}, has been reviewed by {reviewerFullName} ({outcomeNotification.UserEmail}) ({centreName}).
+ {approvalStatus}
+ {commentsText}
+ The full competency assessment review status, can be viewed by visiting: {competencyAssessmentUrl}. Once all of the required reviewers have approved the competency assessment, you may publish it. You will need to login to the Digital Learning Solutions platform to access the competency assessment.",
+ HtmlBody = $@"
+ Dear {outcomeNotification.OwnerFirstName},
+ Your competency assessment, {outcomeNotification.SelfAssessmentName}, has been reviewed by {reviewerFullName} ({centreName}).
+ {approvalStatus}
+ {commentsHtml}
+ Click here to view the full review status for the competency assessment. Once all of the required reviewers have approved the competency assessment, you may publish it.
+ You will need to login to the Digital Learning Solutions platform to access the competency assessment.
+ ",
+ };
+ emailService.SendEmail(new Email(emailSubjectLine, builder, outcomeNotification.OwnerEmail, outcomeNotification.UserEmail));
+ }
public void SendSupervisorDelegateInvite(int supervisorDelegateId, int adminId, int centreId)
{
@@ -220,12 +279,30 @@ public void SendSupervisorDelegateInvite(int supervisorDelegateId, int adminId,
builder.TextBody = $@"Dear colleague,
You have been invited to register to access the NHS England, Digital Learning Solutions platform as a supervised delegate by {supervisorDelegate.SupervisorName} ({supervisorDelegate.SupervisorEmail}) ({centreName}).
To register, visit {dlsUrlBuilder.Uri.ToString()}.
- Registering using this link will confirm your acceptance of the invite. Your supervisor will then be able to assign role profile assessments and view and validate your self assessment results.";
- builder.HtmlBody = $@"Dear colleague,
You have been invited to register to access the NHS England, Digital Learning Solutions platform as a supervised delegate by {supervisorDelegate.SupervisorName} ({centreName}).
Click here to register and confirm your acceptance of the invite.
Your supervisor will then be able to assign role profile assessments and view and validate your self assessment results.
";
+ Registering using this link will confirm your acceptance of the invite. Your supervisor will then be able to assign competency assessments and view and validate your self assessment results.";
+ builder.HtmlBody = $@"Dear colleague,
You have been invited to register to access the NHS England, Digital Learning Solutions platform as a supervised delegate by {supervisorDelegate.SupervisorName} ({centreName}).
Click here to register and confirm your acceptance of the invite.
Your supervisor will then be able to assign competency assessments and view and validate your self assessment results.
";
emailService.SendEmail(new Email(emailSubjectLine, builder, supervisorDelegate.DelegateEmail));
}
}
+ public void SendSupervisorDelegateConfirmed(int supervisorDelegateId, int adminId, int delegateUserId, int centreId)
+ {
+ var supervisorDelegate = supervisorService.GetSupervisorDelegateDetailsById(supervisorDelegateId, adminId, delegateUserId);
+
+ string centreName = GetCentreName(centreId);
+ string emailSubjectLine = "Supervisor Confirmed - Digital Learning Solutions";
+ var builder = new BodyBuilder();
+ builder.TextBody = $@"Dear {supervisorDelegate.FirstName}
+You have been identified as a supervised delegate by {supervisorDelegate.SupervisorName} ({supervisorDelegate.SupervisorEmail}) ({centreName}) in the NHS England, Digital Learning Solutions (DLS) platform.
+
+You are already registered as a delegate at the supervisor's DLS centre so they can now assign competency self assessments and view and validate your self assessment results.
+
+If this looks like a mistake, please contact {supervisorDelegate.SupervisorName} ({supervisorDelegate.SupervisorEmail}) ({centreName}) directly to correct.";
+ builder.HtmlBody = $@"Dear {supervisorDelegate.FirstName}
{supervisorDelegate.SupervisorName} ({centreName}) has accepted your request to be your supervisor for profile asessment activities in the NHS England, Digital Learning Solutions platform.
Click here to access your competency assessments.
";
+ string toEmail = (@adminId == 0 ? supervisorDelegate.DelegateEmail : supervisorDelegate.SupervisorEmail);
+ emailService.SendEmail(new Email(emailSubjectLine, builder, toEmail));
+ }
+
public void SendSupervisorResultReviewed(int adminId, int supervisorDelegateId, int candidateAssessmentId, int resultId, int centreId)
{
var supervisorDelegate = supervisorService.GetSupervisorDelegateDetailsById(supervisorDelegateId, adminId, 0);
@@ -391,8 +468,8 @@ public void SendSupervisorDelegateReminder(int supervisorDelegateId, int adminId
builder.TextBody = $@"Dear colleague,
This is a reminder to to register to access the NHS England, Digital Learning Solutions platform as a supervised delegate by {supervisorDelegate.SupervisorName} ({supervisorDelegate.SupervisorEmail}) ({centreName}).
To register, visit {dlsUrlBuilder.Uri.ToString()}.
- Your supervisor will then be able to assign role profile assessments and view and validate your self assessment results.";
- builder.HtmlBody = $@"Dear colleague,
This is a reminder to register to access the NHS England, Digital Learning Solutions platform as a supervised delegate by {supervisorDelegate.SupervisorName} ({centreName}).
Click here to register.
Your supervisor will then be able to assign role profile assessments and view and validate your self assessment results.
";
+ Your supervisor will then be able to assign competency assessments and view and validate your self assessment results.";
+ builder.HtmlBody = $@"Dear colleague,
This is a reminder to register to access the NHS England, Digital Learning Solutions platform as a supervised delegate by {supervisorDelegate.SupervisorName} ({centreName}).
Click here to register.
Your supervisor will then be able to assign competency assessments and view and validate your self assessment results.
";
emailService.SendEmail(new Email(emailSubjectLine, builder, supervisorDelegate.DelegateEmail));
}
}
diff --git a/DigitalLearningSolutions.Web/Services/FrameworkService.cs b/DigitalLearningSolutions.Web/Services/FrameworkService.cs
index 6d904a9eeb..81a3cd1a8f 100644
--- a/DigitalLearningSolutions.Web/Services/FrameworkService.cs
+++ b/DigitalLearningSolutions.Web/Services/FrameworkService.cs
@@ -46,9 +46,9 @@ public interface IFrameworkService
CollaboratorNotification? GetCollaboratorNotification(int id, int invitedByAdminId);
// Competencies/groups:
- IEnumerable GetFrameworkCompetencyGroups(int frameworkId);
+ IEnumerable GetFrameworkCompetencyGroups(int frameworkId, int? assessmentId);
- IEnumerable GetFrameworkCompetenciesUngrouped(int frameworkId);
+ IEnumerable GetFrameworkCompetenciesUngrouped(int frameworkId, int? assessmentId);
CompetencyGroupBase? GetCompetencyGroupBaseById(int Id);
@@ -484,9 +484,9 @@ public IEnumerable GetFrameworkByFrameworkName(string framewor
return frameworkDataService.GetFrameworkByFrameworkName(frameworkName, adminId);
}
- public IEnumerable GetFrameworkCompetenciesUngrouped(int frameworkId)
+ public IEnumerable GetFrameworkCompetenciesUngrouped(int frameworkId, int? assessmentId)
{
- return frameworkDataService.GetFrameworkCompetenciesUngrouped(frameworkId);
+ return frameworkDataService.GetFrameworkCompetenciesUngrouped(frameworkId, assessmentId);
}
public FrameworkCompetency? GetFrameworkCompetencyById(int Id)
@@ -499,9 +499,9 @@ public IEnumerable GetFrameworkCompetenciesUngrouped(int fr
return frameworkDataService.GetFrameworkCompetencyForPreview(frameworkCompetencyId);
}
- public IEnumerable GetFrameworkCompetencyGroups(int frameworkId)
+ public IEnumerable GetFrameworkCompetencyGroups(int frameworkId, int? assessmentId)
{
- return frameworkDataService.GetFrameworkCompetencyGroups(frameworkId);
+ return frameworkDataService.GetFrameworkCompetencyGroups(frameworkId, assessmentId);
}
public string? GetFrameworkConfigForFrameworkId(int frameworkId)
@@ -606,12 +606,12 @@ public int InsertComment(int frameworkId, int adminId, string comment, int? repl
public int InsertCompetency(string name, string? description, int adminId, bool alwaysShowDescription = false)
{
- return frameworkDataService.InsertCompetency(name, description, adminId, alwaysShowDescription);
+ return frameworkDataService.InsertCompetency(name.Trim(), description, adminId, alwaysShowDescription);
}
public int InsertCompetencyGroup(string groupName, string? groupDescription, int adminId, int? frameworkId)
{
- return frameworkDataService.InsertCompetencyGroup(groupName, groupDescription, adminId, frameworkId);
+ return frameworkDataService.InsertCompetencyGroup(groupName.Trim(), groupDescription, adminId, frameworkId);
}
public int InsertFrameworkCompetency(int competencyId, int? frameworkCompetencyGroupID, int adminId, int frameworkId, bool addDefaultQuestions = true)
@@ -686,12 +686,12 @@ public int UpdateCompetencyFlags(int frameworkId, int competencyId, int[] select
public void UpdateFrameworkCompetency(int frameworkCompetencyId, string name, string? description, int adminId, bool? alwaysShowDescription)
{
- frameworkDataService.UpdateFrameworkCompetency(frameworkCompetencyId, name, description, adminId, alwaysShowDescription);
+ frameworkDataService.UpdateFrameworkCompetency(frameworkCompetencyId, name.Trim(), description, adminId, alwaysShowDescription);
}
public bool UpdateFrameworkCompetencyGroup(int frameworkCompetencyGroupId, int competencyGroupId, string name, string? description, int adminId)
{
- return frameworkDataService.UpdateFrameworkCompetencyGroup(frameworkCompetencyGroupId, competencyGroupId, name, description, adminId);
+ return frameworkDataService.UpdateFrameworkCompetencyGroup(frameworkCompetencyGroupId, competencyGroupId, name.Trim(), description, adminId);
}
public void UpdateFrameworkConfig(int frameworkId, int adminId, string? frameworkConfig)
diff --git a/DigitalLearningSolutions.Web/Services/GroupsService.cs b/DigitalLearningSolutions.Web/Services/GroupsService.cs
index 176bc15997..013a08963b 100644
--- a/DigitalLearningSolutions.Web/Services/GroupsService.cs
+++ b/DigitalLearningSolutions.Web/Services/GroupsService.cs
@@ -721,7 +721,11 @@ IEnumerable delegateNotificationPreferences
delegateUserId,
groupCourse.CustomisationId
);
-
+ // If the delegate has already completed this course, do nothing
+ if (candidateProgressOnCourse.Any(p => p.Completed != null))
+ {
+ return;
+ }
var existingRecordsToUpdate = candidateProgressOnCourse.Where(
p => ProgressShouldBeUpdatedOnEnrolment(p, isAddCourseToGroup)
).ToList();
diff --git a/DigitalLearningSolutions.Web/Services/ImportCompetenciesFromFileService.cs b/DigitalLearningSolutions.Web/Services/ImportCompetenciesFromFileService.cs
index 0b4b100db4..a1987466e2 100644
--- a/DigitalLearningSolutions.Web/Services/ImportCompetenciesFromFileService.cs
+++ b/DigitalLearningSolutions.Web/Services/ImportCompetenciesFromFileService.cs
@@ -37,7 +37,7 @@ public ImportCompetenciesResult PreProcessCompetenciesTable(IXLWorkbook workbook
var newCompetencyIds = competencyRows.Select(row => row.ID ?? 0).ToList();
var existingIds = frameworkService.GetFrameworkCompetencyOrder(frameworkId, newCompetencyIds);
var existingGroups = frameworkService
- .GetFrameworkCompetencyGroups(frameworkId).Where(x => x.FrameworkCompetencies.Any()).ToList()
+ .GetFrameworkCompetencyGroups(frameworkId, null).Where(x => x.FrameworkCompetencies.Any()).ToList()
.Select(row => row.Name)
.Distinct()
.ToList();
@@ -152,7 +152,7 @@ internal ImportCompetenciesResult ProcessCompetenciesTable(IXLTable table, int a
.ToList();
for (int i = 0; i < competencyGroupCount; i++)
{
- var existingGroups = frameworkService.GetFrameworkCompetencyGroups(frameworkId).Select(row => new { row.ID, row.Name })
+ var existingGroups = frameworkService.GetFrameworkCompetencyGroups(frameworkId, null).Select(row => new { row.ID, row.Name })
.Distinct()
.ToList();
var placesToMove = Math.Abs(existingGroups.FindIndex(group => group.Name == distinctCompetencyGroups[i]) - i);
@@ -208,7 +208,6 @@ CompetencyTableRow competencyRow
else
{
frameworkCompetencyGroupId = frameworkService.GetFrameworkCompetencyGroupId(frameworkId, newCompetencyGroupId);
-
var isUpdated = frameworkService.UpdateFrameworkCompetencyGroup((int)frameworkCompetencyGroupId, newCompetencyGroupId, competencyRow.CompetencyGroup, competencyRow.GroupDescription, adminId);
competencyRow.RowStatus = RowStatus.CompetencyGroupUpdated;
}
diff --git a/DigitalLearningSolutions.Web/Services/RoleProfileService.cs b/DigitalLearningSolutions.Web/Services/RoleProfileService.cs
deleted file mode 100644
index 7983b2afa4..0000000000
--- a/DigitalLearningSolutions.Web/Services/RoleProfileService.cs
+++ /dev/null
@@ -1,68 +0,0 @@
-using DigitalLearningSolutions.Data.DataServices;
-using DigitalLearningSolutions.Data.Models.RoleProfiles;
-using System.Collections.Generic;
-
-namespace DigitalLearningSolutions.Web.Services
-{
- public interface IRoleProfileService
- {
- //GET DATA
- IEnumerable GetAllRoleProfiles(int adminId);
-
- IEnumerable GetRoleProfilesForAdminId(int adminId);
-
- RoleProfileBase? GetRoleProfileBaseById(int roleProfileId, int adminId);
-
- RoleProfileBase? GetRoleProfileByName(string roleProfileName, int adminId);
-
- IEnumerable GetNRPProfessionalGroups();
-
- //UPDATE DATA
- bool UpdateRoleProfileName(int roleProfileId, int adminId, string roleProfileName);
-
- bool UpdateRoleProfileProfessionalGroup(int roleProfileId, int adminId, int? nrpProfessionalGroupID);
-
- }
- public class RoleProfileService : IRoleProfileService
- {
- private readonly IRoleProfileDataService roleProfileDataService;
- public RoleProfileService(IRoleProfileDataService roleProfileDataService)
- {
- this.roleProfileDataService = roleProfileDataService;
- }
- public IEnumerable GetAllRoleProfiles(int adminId)
- {
- return roleProfileDataService.GetAllRoleProfiles(adminId);
- }
-
- public IEnumerable