From 6b21cd7f8026303a398272780b1c5ce2dd7dd00e Mon Sep 17 00:00:00 2001 From: Heiko Bernloehr Date: Thu, 3 Apr 2014 00:35:46 +0200 Subject: [PATCH] Major update. Added UML state diagram. All over improvements. --- Makefile | 11 +- cc_enrolment_status.schema.json | 2 +- enrolment_status.tex | 164 +++++++++++------- enrolment_status_seq.pic | 105 ++++++------ enrolment_status_state.uxf | 321 ++++++++++++++++++++++++++++++++++++ subsequent_enrolment_status_seq.pic | 101 ++++++------ 6 files changed, 546 insertions(+), 158 deletions(-) rewrite enrolment_status_seq.pic (72%) create mode 100644 enrolment_status_state.uxf rewrite subsequent_enrolment_status_seq.pic (60%) diff --git a/Makefile b/Makefile index e93b8ac..ee23b0d 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ TEXFILES=$(basename $(filter-out %.inc.tex, $(wildcard *.tex))) -.PHONY: dvi ps pdf clean vps vdvi ftp +.PHONY: dvi ps pdf clean vps vdvi ftp uxf AUX_FILES=$(addsuffix .aux,$(TEXFILES)) LOG_FILES=$(addsuffix .log,$(TEXFILES)) @@ -10,6 +10,7 @@ PDF_FILES=$(addsuffix .pdf,$(TEXFILES)) $(filter-out $(mains),$(objects)) PIC_FILES=$(filter-out sequence.pic, $(wildcard *.pic)) FIG_FILES=$(wildcard *.fig) +UXF_FILES=$(wildcard *.uxf) dvi: $(DVI_FILES) ps: $(PS_FILES) @@ -25,7 +26,10 @@ clean: rm -f $(wildcard *.aux) rm -f $(wildcard *.out) rm -f $(wildcard *.log) + rm -f $(wildcard *.fff) + rm -f $(wildcard *.lof) rm -f $(addsuffix .eps, $(basename $(PIC_FILES))) + rm -f $(addsuffix .eps, $(basename $(UXF_FILES))) rm -f $(addsuffix .eps, $(basename $(FIG_FILES))) rm -f gitHeadInfo.gin @@ -50,10 +54,13 @@ $(addsuffix .eps, $(basename $(PIC_FILES))):%.eps:%.pic $(addsuffix .eps, $(basename $(FIG_FILES))):%.eps:%.fig fig2dev -Leps $< $@ +$(addsuffix .eps, $(basename $(UXF_FILES))):%.eps:%.uxf + umlet.sh -action=convert -format=eps -filename=$< + gitHeadInfo.gin: ./generate_gitinfo.sh -$(DVI_FILES):%.dvi:%.tex $(addsuffix .eps, $(basename $(PIC_FILES))) $(addsuffix .eps, $(basename $(FIG_FILES))) gitHeadInfo.gin cc_enrolment_status.schema.json +$(DVI_FILES):%.dvi:%.tex $(addsuffix .eps, $(basename $(PIC_FILES))) $(addsuffix .eps, $(basename $(FIG_FILES))) $(addsuffix .eps, $(basename $(UXF_FILES))) gitHeadInfo.gin cc_enrolment_status.schema.json echo $(VERSION) | latex $< echo $(VERSION) | latex $< diff --git a/cc_enrolment_status.schema.json b/cc_enrolment_status.schema.json index 0cd74e7..9deff91 100644 --- a/cc_enrolment_status.schema.json +++ b/cc_enrolment_status.schema.json @@ -7,7 +7,7 @@ "externalCourseID": { "type":"string", "required":false }, "personID": { "type":"string", "required":true }, "personIDtype": { "enum": [ "matriculation", "eppn", "generic1", "generic2", "generic3" ], "required":true }, - "status": { "enum": [ "active", "pending", "rejected", "unsubscribed", "denied", "nostatus" ], "required":true } + "status": { "enum": [ "active", "pending", "denied", "rejected", "unsubscribed", "deleted", "account_deactivated" ], "required":true } }, "additionalProperties" : false } diff --git a/enrolment_status.tex b/enrolment_status.tex index c5cbba1..7f38fa4 100644 --- a/enrolment_status.tex +++ b/enrolment_status.tex @@ -54,8 +54,9 @@ \newcommand{\texte}{} \newcommand{\textf}{} -\begin{document} \linenumbers + +\begin{document} \title{Mahara/Moodle enrolment status} \author{David Boehringer\\Universit\"at Stuttgart \and Heiko Bernl\"ohr\\FreeIT.de} \maketitle @@ -65,7 +66,7 @@ % Commit-id: \gitAbbrevHash \end{center} \begin{abstract} -New ECS resource providing enrolment status for users subscribed/enroled to +New ECS resource providing enrolment status for users enroled to external courses (via courselinks). The course providing participant (e.g. Moodle) informs the courselink providing participant of the subscribed user (e.g. Mahara) about every change of its enrolment status in the course. @@ -82,48 +83,84 @@ Moodle) informs the courselink providing participant of the subscribed user \section{Enrolment status} The \cces resource describes an enrolment status of a -remotly subscribed user (via courselinks) to a course. - -\subsection{Events and status values} +remotly subscribed user (via courselinks) to a course. This document supersedes +the part +\begin{quote} + {\bf Moodle}\\ + 1. New enrolmentStatus resource +\end{quote} +in \cite{specBoehMaharaMoodle}. + +\subsection{Events\label{events}} +\newcommand{\rstat}[1]{return status: #1} +For the following event considerations please have always the state chart on +page \pageref{state:enrolmentStatus} in mind.\medskip Moodle has to send the user enrolment status to Mahara on these events: -\begin{itemize} - \item A user clicks on a courselink on Mahara for the first time and tries to - consume the linked course on Moodle (sequence diagram on - page \pageref{seq:initial_enrolment_status}).\\ - \texttt{status $\rightarrow$ active | pending | rejected } - \begin{description} - \item[active] User is enroled and is allowed to consume the course. - \item[pending] User enroled for the course, but is on a waiting list. - \item[rejected] User was on a waiting list, but had not been accepted for - the course. This status is usually not persistently stored in the LMS. - \end{description} - \item\begin{description} - \item[unsubscribed] User ended course membership himself or his membership - has been terminated by course administrator or system - administrator. This status is usually not persistently stored in the - LMS.\end{description}% - \texttt{status $\rightarrow$ unsubscribed} - \item\begin{description} - \item[denied] User is marked by course administrator not to be allowed to - access course for whatever reason.\end{description}% - \texttt{status $\rightarrow$ denied} - \item\begin{description} - \item[deleted] User account on remote system is deactivated or deleted. - This status is usually not persistently stored in the - LMS.\end{description}% - \texttt{status $\rightarrow$ deleted} - \item A user subsequently clicks a courselink on Mahara and consumes an already - subscribed course on Moodle (see page \pageref{seq:subsequent_enrolment_status}). - This guarantees to have a status even if Mahara just - forget/loose\footnote{We don't make the resource persistent, so loosing - the status somehow on Mahara it will be refreshed after next click on a - courselink.} it somehow. Moodle just returns the current status.\\ - \texttt{status $\rightarrow$ active | denied | unsubscribed | deleted | nostatus } - \begin{description} - \item[nostatus] Initial status before user ever tried to enrole. - \end{description} -\end{itemize} +\begin{description} + \item[first time clicking courselink] A user clicks on a courselink on Mahara + for the first time and tries to consume the linked course on Moodle (sequence + diagram on page \pageref{seq:initialEnrolmentStatus}).\\ + \rstat{active | pending } + \item[reject] User will be rejected by course or system admin. This could only happen + in \textit{pending} state, e.g. user was on a waiting list, but had not been + accepted for the course.\\ + \rstat{rejected} + \item[subscribe] User will be subcribed to course (leaving waiting list). Now + he is able to access and consume the course.\\ + \rstat{active} + \item[go pending] User will be transfered to waiting list + i.e. he can't access and consume the course yet. He has to wait until + he will be subscribed to the course.\\ + \rstat{pending} + \item[unsubscribe] User ended course membership himself or has been + terminated by course administrator or system administrator. To access and + consume the course again he has to enrole from scratch.\\ + \rstat{unsubscribed} + \item[disable subscription] User is disabled by course or system + administrator, i.e. he is (temporarly) not allowed to access course for + whatever reason.\\ + \rstat{denied} + \item[enable subscription] User is enabled by course or system administrator, + i.e. he is to be allowed to access course again.\\ + \rstat{active} + \item[deactivate user account] User account on remote system is deactivated.\\ + \rstat{inactive\_account} + \item[activate user account] User account on remote system is activated.\\ + \rstat{active | pending | denied} + \item[subsequently clicking courselink] A user subsequently clicks a + courselink on Mahara and consumes an already enroled course on Moodle (see + page \pageref{seq:subsequentEnrolmentStatus}). After this event Moodle has + to send the current enrolment status. This also guarantees to have a + status even if Mahara just forget/loose\footnote{We don't make the resource + persistent, so loosing the status somehow on Mahara it will be refreshed + after next click on a courselink.} it somehow.\\ + \rstat{pending | active | denied } +\end{description} + +\subsection{Status values\label{statusValues}} +\newcommand{\statusnotstored}{This status is usually not persistently stored in the LMS.} +For the following status considerations please have always the state chart on +page \pageref{state:enrolmentStatus} in mind.\medskip + +The status attribute could have following values: +\begin{description} + \item[active] User is enroled and allowed to consume the course. + \item[pending] User enroled for the course, but is on a waiting list. + \item[rejected] User is not enroled anymore\footnote{Clicking the courselink + again the user will get through initial enrolment as described in sequence + diagram on page \pageref{seq:initialEnrolmentStatus}.}. \statusnotstored + \item[unsubscribed] User is not enroled + anymore\addtocounter{footnote}{-1}\footnotemark. \statusnotstored + \item[denied] Still enroled but not allowed to access the course. + \item[inactive\_account] Still enroled but temporarly not allowed to access + the system. + \item[deleted] User is not enroled + anymore\addtocounter{footnote}{-1}\footnotemark. \statusnotstored +\end{description} +Whereas only \texttt{active, pending, rejected} and \texttt{inactive\_account} +status values have to be stored/saved on the LMS. All others are only generated +while the transitions. \subsection{Redirect querystring parameters\label{subsec:redirect_querystring_parameters}} @@ -138,33 +175,48 @@ have to enhance our specs in \cite{specRedirectProcedure} and \begin{figure}[p] \includegraphics[scale=0.8]{enrolment_status_seq} - \caption{\label{seq:initial_enrolment_status}Sending enrolment status - after initially consuming a courselink. For the \textit{params} in the - \textit{redirect} of the \textit{consume courselink} block see subsection - \ref{subsec:redirect_querystring_parameters} at page - \pageref{subsec:redirect_querystring_parameters}.} + \caption{\label{seq:initialEnrolmentStatus}Sending enrolment status after + consuming a courselink for the first time. For \textit{params} in + the \textit{redirect} of the \textit{consume courselink} block see + \ref{subsec:redirect_querystring_parameters} at page + \pageref{subsec:redirect_querystring_parameters}.} \end{figure} \begin{figure}[p] \includegraphics[scale=0.8]{subsequent_enrolment_status_seq} - \caption{\label{seq:subsequent_enrolment_status}Sending enrolment status - after subsequently consuming a courselink. The \textit{consume courselink} block is the same as in figure \ref{seq:initial_enrolment_status} on page \pageref{seq:initial_enrolment_status}.} + \caption{\label{seq:subsequentEnrolmentStatus}Sending enrolment status + after subsequently consuming a courselink.} \end{figure} +\begin{figure}[p] + \centering + \includegraphics[scale=0.5]{enrolment_status_state} + \caption{\label{state:enrolmentStatus}UML state diagram. Enrolment status of + a user in respect to a course on a LMS. For event description see + \ref{events} on page \pageref{events}. For status values description see + \ref{statusValues} on page \pageref{statusValues}.} +\end{figure} + + \subsection{Resource attributes} For the json schema of \cces see page \pageref{schema:enrolment_status}. Following description write on \cces resource attributes for which a user status has changed or asked for: \begin{description} - \item[courseURL] It's the course url. It's also used as a unique id. - \item[personID] Unique id of the user. + \item[courseURL] It's a url transfering a courselink consuming user + directly to the course on the course providing system. It's also used as a + unique id. + \item[courseID] It's a unique course id on the course providing system. It + references the same course as \texttt{courseURL}. + \item[personID] Unique id of the course consuming user. \item[personIDtype] Class/type of \texttt{personID}. It's a controlled - vocabulary / enumeration (see page \pageref{schema:enrolment_status} for - possible values). - \item[status] Status of the user's course subscription. It's a controlled - vocabulary / enumeration (see page \pageref{schema:enrolment_status} for - possible values). + vocabulary (see page \pageref{schema:enrolment_status} for possible + values). + \item[status] Status of the user's course enrolment. It's a controlled + vocabulary (for possible values see schema on page + \pageref{schema:enrolment_status} and values descriptions at + \ref{statusValues}). \end{description} \begin{sidewaysfigure}[p] diff --git a/enrolment_status_seq.pic b/enrolment_status_seq.pic dissimilarity index 72% index 2438af7..de3d1ef 100644 --- a/enrolment_status_seq.pic +++ b/enrolment_status_seq.pic @@ -1,51 +1,54 @@ - -.PS -copy "sequence.pic"; - -movewid = 1; -maxpsht = 15; -maxpswid = 15; -spacing = 0.2; - -# Define objects -object(Moodle,"Moodle:LMS"); -object(ECS,":ECS"); -object(Mahara,"Mahara:PLE"); -actor(A,""); -step();step(); - -# Message sequences - -begin_frame(Moodle,F,"create courselink"); -message(Moodle,ECS,"POST:cc/courselinks"); active(Moodle); active(ECS); step(); inactive(Moodle); inactive(ECS); -message(Mahara,ECS,"*POST:sys/events/fifo"); active(Mahara); active(ECS); step(); inactive(Mahara); inactive(ECS); -message(Mahara,ECS,"GET:cc/courselinks/"); active(Mahara); active(ECS); step(); inactive(ECS); -message(Mahara,Mahara,"create courselinks"); inactive(Mahara); step(); -end_frame(A,F); -step(); -begin_frame(Moodle,F,"consume courselink"); -message(A,Mahara,"click courselink"); active(Mahara);step(); inactive(Mahara); -message(A,Moodle,"redirect(params)"); active(Moodle);step(); -message(Moodle,ECS,"auth:=DELETE:sys/auths/");active(ECS)step();inactive(ECS); -message(Moodle,Moodle,"check auth token"); step(); -message(Moodle,Moodle,"check security hash"); -message(A,Moodle,"confirm enrolment to course"); step(); -message(Moodle,Moodle,"enrole user to course"); step(); -end_frame(A,F); -step(); -begin_frame(Moodle,F,"create enrolm. stat."); -step(); -message(Moodle,ECS,"POST:cc/enrolment_status"); active(ECS); step(); inactive(ECS); inactive(Moodle); -message(Mahara,ECS,"*POST:sys/events/fifo"); active(Mahara); active(ECS); step(); inactive(Mahara); inactive(ECS); -message(Mahara,ECS,"DELETE:cc/enrolement_status/"); active(Mahara); active(ECS); step(); inactive(ECS); -message(Mahara,Mahara,"update status"); inactive(Mahara); step(); -end_frame(A,F); - -# Complete the lifelines - -complete(Moodle); -complete(ECS); -complete(Mahara); -complete(A); -.PE - + +.PS +copy "sequence.pic"; + +movewid = 1; +maxpsht = 15; +maxpswid = 15; +spacing = 0.2; + +# Define objects +object(Moodle,"Moodle:LMS"); +object(ECS,":ECS"); +object(Mahara,"Mahara:PLE"); +actor(A,""); +step();step(); + +# Message sequences + +begin_frame(Moodle,F,"create courselink"); + message(Moodle,ECS,"POST:cc/courselinks"); active(Moodle); active(ECS); step(); inactive(Moodle); inactive(ECS); + message(Mahara,ECS,"*POST:sys/events/fifo"); active(Mahara); active(ECS); step(); inactive(Mahara); inactive(ECS); + message(Mahara,ECS,"GET:cc/courselinks/"); active(Mahara); active(ECS); step(); inactive(ECS); + message(Mahara,Mahara,"create courselinks"); inactive(Mahara); step(); +end_frame(A,F); +step(); +begin_frame(Moodle,F,"consume courselink"); + message(A,Mahara,"click courselink"); active(Mahara);step(); inactive(Mahara); + message(A,Moodle,"redirect(params)"); active(Moodle);step(); + message(Moodle,ECS,"auth:=DELETE:sys/auths/");active(ECS)step();inactive(ECS); + message(Moodle,Moodle,"check auth token"); step(); + message(Moodle,Moodle,"check security hash"); + message(A,Moodle,"confirm enrolment to course"); step(); + message(Moodle,Moodle,"enrole user to course"); step(); + active(Moodle); + message(Moodle,A,"*provide course");step(); + inactive(Moodle);step(); +end_frame(A,F); +step(); +begin_frame(Moodle,F,"send status"); + step(); + message(Moodle,ECS,"POST:cc/enrolment_status"); active(ECS); step(); inactive(ECS); inactive(Moodle); + message(Mahara,ECS,"*POST:sys/events/fifo"); active(Mahara); active(ECS); step(); inactive(Mahara); inactive(ECS); + message(Mahara,ECS,"DELETE:cc/enrolement_status/"); active(Mahara); active(ECS); step(); inactive(ECS); + message(Mahara,Mahara,"update status"); inactive(Mahara); step(); +end_frame(A,F); + +# Complete the lifelines + +complete(Moodle); +complete(ECS); +complete(Mahara); +complete(A); +.PE + diff --git a/enrolment_status_state.uxf b/enrolment_status_state.uxf new file mode 100644 index 0000000..d93a08b --- /dev/null +++ b/enrolment_status_state.uxf @@ -0,0 +1,321 @@ + + + 10 + + com.umlet.element.custom.State + + 130 + 550 + 160 + 40 + + pending +entry/ send(pending) +layer=2 +bg=default + + + + com.umlet.element.custom.State + + 470 + 550 + 160 + 40 + + active +entry/ send(active) +layer=2 +bg=default + + + + com.umlet.element.Relation + + 210 + 380 + 290 + 190 + + lt=<- +go pending +[no seat +available] +layer=2 + 30;170;270;30 + + + com.umlet.element.Relation + + 440 + 390 + 126 + 180 + + lt=<- +subscribe +[seat +available] +layer=2 + 70;160;70;30 + + + com.umlet.element.Relation + + 260 + 500 + 230 + 70 + + lt=<- +subscribe +layer=2 + 210;50;30;50 + + + com.umlet.element.custom.State + + 470 + 670 + 160 + 40 + + denied +entry/ send(denied) +layer=2 +bg=default + + + + com.umlet.element.Relation + + 520 + 560 + 142 + 130 + + lt=<- +disable +subscription +layer=2 + 80;110;80;30 + + + com.umlet.element.Relation + + 110 + 560 + 150 + 130 + + lt=<- +reject/ +send(rejected) +layer=2 + 80;110;80;30 + + + com.umlet.element.Relation + + 420 + 560 + 142 + 130 + + lt=<- +enable +subscription +layer=2 + 80;30;80;110 + + + com.umlet.element.custom.FinalState + + 180 + 670 + 20 + 20 + + layer=2 + + + + com.umlet.element.Relation + + 260 + 540 + 230 + 70 + + lt=<- +go pending +layer=2 + 30;50;210;50 + + + com.umlet.element.custom.State + + 100 + 340 + 560 + 400 + + active user account +-- +layer=1 + + + + com.umlet.element.Relation + + 380 + 710 + 170 + 140 + + lt=<- +account deletion/ +send(deleted) +layer=1 + 90;120;90;30 + + + com.umlet.element.custom.FinalState + + 460 + 830 + 20 + 20 + + layer=1 + + + + com.umlet.element.Relation + + 510 + 710 + 196 + 140 + + lt=<- +unsubscribe/ +send(unsubscribed) +layer=1 + 100;120;100;30 + + + com.umlet.element.custom.FinalState + + 600 + 830 + 20 + 20 + + layer=1 + + + + com.umlet.element.Relation + + 160 + 710 + 202 + 152 + + lt=<- +subsequently +clicking +courselink/ +send_current_state() +layer=1 + 30;30;30;80;180;80;180;30 + + + com.umlet.element.custom.Decision + + 480 + 390 + 40 + 40 + + layer=2 + + + + com.umlet.element.Relation + + 410 + 210 + 178 + 200 + + lt=<- +first time clicking +courselink +layer=2 + 90;180;90;30 + + + com.umlet.element.custom.InitialState + + 490 + 220 + 20 + 20 + + layer=2 + + + + com.umlet.element.custom.State + + 100 + 160 + 230 + 50 + + inactive_account +entry/ send(inactive_account) +bg=default + + + + com.umlet.element.Relation + + 180 + 180 + 148 + 180 + + lt=<- +deactivate +user account + 80;30;80;160 + + + com.umlet.element.Relation + + 90 + 180 + 148 + 240 + + lt=<- +activate +user account +layer=2 + 80;220;80;30 + + + com.umlet.element.custom.HistoryState + + 160 + 400 + 20 + 20 + + layer=2 + + + diff --git a/subsequent_enrolment_status_seq.pic b/subsequent_enrolment_status_seq.pic dissimilarity index 60% index 2e5c4cf..e9b944c 100644 --- a/subsequent_enrolment_status_seq.pic +++ b/subsequent_enrolment_status_seq.pic @@ -1,48 +1,53 @@ -.PS -copy "sequence.pic"; - -movewid = 1; -maxpsht = 15; -maxpswid = 15; -spacing = 0.2; - -# Define objects - -object(Moodle,"Moodle:LMS"); -object(ECS,":ECS"); -object(Mahara,"Mahara:PLE"); -actor(A,""); -step();step(); - -# Message sequences - -begin_frame(Moodle,F,"consume courselink"); -step(); -step(); -step(); -end_frame(A,F); -step(); -begin_frame(Moodle,F,"create enrolm. stat."); -step(); -# -message(Moodle,ECS,"POST:cc/enrolment_status"); -comment(ECS,C1,down 0.5 left 0.3, wid 1.8 ht .5 "we always POST here" "because we DELETE" "there (nonpersistent)"); -active(Moodle); active(ECS); step(); inactive(ECS); inactive(Moodle); -# -message(Mahara,ECS,"*POST:sys/events/fifo"); active(Mahara); active(ECS); step(); inactive(Mahara); inactive(ECS); -# -message(Mahara,ECS,"DELETE:cc/enrolement_status/"); -connect_to_comment(ECS,C1); -active(Mahara); active(ECS); step(); inactive(ECS); -# -message(Mahara,Mahara,"update status"); inactive(Mahara); step(); -end_frame(A,F); - -# Complete the lifelines - -complete(Moodle); -complete(ECS); -complete(Mahara); -complete(A); -.PE - +.PS +copy "sequence.pic"; + +movewid = 1; +maxpsht = 15; +maxpswid = 15; +spacing = 0.2; + +# Define objects + +object(Moodle,"Moodle:LMS"); +object(ECS,":ECS"); +object(Mahara,"Mahara:PLE"); +actor(A,""); +step();step(); + +# Message sequences + +begin_frame(Moodle,F,"consume courselink"); + message(A,Mahara,"click courselink"); active(Mahara);step(); inactive(Mahara); + message(A,Moodle,"redirect(params)"); active(Moodle);step(); + message(Moodle,ECS,"auth:=DELETE:sys/auths/");active(ECS)step();inactive(ECS); + message(Moodle,Moodle,"check auth token"); step(); + message(Moodle,Moodle,"check security hash"); step(); + active(Moodle); + message(Moodle,A,"*provide course");step(); + inactive(Moodle);step(); +end_frame(A,F); +step(); +begin_frame(Moodle,F,"send status"); + step(); + # + message(Moodle,ECS,"POST:cc/enrolment_status"); + comment(ECS,C1,down 0.5 left 0.3, wid 1.8 ht .5 "we always POST here" "because we DELETE" "there (nonpersistent)"); + active(ECS); step(); inactive(ECS); inactive(Moodle); + # + message(Mahara,ECS,"*POST:sys/events/fifo"); active(Mahara); active(ECS); step(); inactive(Mahara); inactive(ECS); + # + message(Mahara,ECS,"DELETE:cc/enrolement_status/"); + connect_to_comment(ECS,C1); + active(Mahara); active(ECS); step(); inactive(ECS); + # + message(Mahara,Mahara,"update status"); inactive(Mahara); step(); +end_frame(A,F); + +# Complete the lifelines + +complete(Moodle); +complete(ECS); +complete(Mahara); +complete(A); +.PE + -- 2.11.4.GIT