diff options
-rw-r--r-- | .gitignore | 39 | ||||
-rw-r--r-- | assignment-7/appointments.icl | 54 | ||||
-rw-r--r-- | assignment-7/assignment-7.tex | 117 | ||||
-rw-r--r-- | assignment-7/make.png | bin | 0 -> 63307 bytes | |||
-rw-r--r-- | assignment-7/propose-avail.png | bin | 0 -> 79841 bytes | |||
-rw-r--r-- | assignment-7/propose-manage.png | bin | 0 -> 90049 bytes | |||
-rw-r--r-- | assignment-7/propose.png | bin | 0 -> 74644 bytes | |||
-rw-r--r-- | assignment-7/show-list.png | bin | 0 -> 69840 bytes | |||
-rw-r--r-- | assignment-7/show-svg.png | bin | 0 -> 83684 bytes | |||
-rw-r--r-- | assignment-7/user-early.png | bin | 0 -> 62760 bytes | |||
-rw-r--r-- | assignment-7/user-task.png | bin | 0 -> 62347 bytes |
11 files changed, 186 insertions, 24 deletions
@@ -1,3 +1,4 @@ +# Clean Clean\ System\ Files/ a.out *-www/ @@ -5,3 +6,41 @@ a.out *-data/ assignment-5/skeleton5 assignment-6/multiplechoice + +# TeX +*-blx.bib +*.acn +*.acr +*.alg +*.aux +*.bbl +*.bcf +*.blg +*.dvi +*.ent +*.fdb_latexmk +*.fls +*.fmt +*.glg +*.glo +*.gls +*.idx +*.ilg +*.ind +*.ist +*.lof +*.log +*.lot +*.maf +*.mtc +*.mtc1 +*.nav +*.out +*.pdf +*.run.xml +*.snm +*.swp +*.synctex.gz +*.toc +_minted-*/ +*-tags.tex diff --git a/assignment-7/appointments.icl b/assignment-7/appointments.icl index 54b2d86..c65ed71 100644 --- a/assignment-7/appointments.icl +++ b/assignment-7/appointments.icl @@ -45,16 +45,19 @@ derive class iTask Appointment derive JSEncode Appointment, User, Time, DateTime, Maybe instance == Appointment where == a b = a === b +// Some examples. Change the day fields (+7) if working later than 2017-11-18. +// For these tasks, no user tasks are created (this is done in addAppointment). +// They are just here to make it easier to develop the SVG calendar. appointments :: Shared [Appointment] appointments = sharedStore "appointments" - [ {title="Advanced Programming", when={DateTime|year=2017,mon=11,day= 6,hour=10,min=30,sec=0}, duration={Time|hour=2,min=0,sec=0}, owner=AuthenticatedUser "root" ["admin","manager"] (Just "Root user"), participants=[]} - , {title="Information Retrieval", when={DateTime|year=2017,mon=11,day= 6,hour=15,min=30,sec=0}, duration={Time|hour=2,min=0,sec=0}, owner=AuthenticatedUser "root" ["admin","manager"] (Just "Root user"), participants=[]} - , {title="Cultural Contacts", when={DateTime|year=2017,mon=11,day= 7,hour=13,min= 0,sec=0}, duration={Time|hour=2,min=0,sec=0}, owner=AuthenticatedUser "root" ["admin","manager"] (Just "Root user"), participants=[]} - , {title="Historical Grammar", when={DateTime|year=2017,mon=11,day= 7,hour=15,min= 0,sec=0}, duration={Time|hour=2,min=0,sec=0}, owner=AuthenticatedUser "root" ["admin","manager"] (Just "Root user"), participants=[]} - , {title="Text Mining", when={DateTime|year=2017,mon=11,day= 8,hour= 8,min=30,sec=0}, duration={Time|hour=2,min=0,sec=0}, owner=AuthenticatedUser "root" ["admin","manager"] (Just "Root user"), participants=[]} - , {title="Testing Techniques", when={DateTime|year=2017,mon=11,day= 8,hour= 8,min=30,sec=0}, duration={Time|hour=2,min=0,sec=0}, owner=AuthenticatedUser "root" ["admin","manager"] (Just "Root user"), participants=[]} - , {title="Testing Techniques", when={DateTime|year=2017,mon=11,day=10,hour= 8,min=30,sec=0}, duration={Time|hour=2,min=0,sec=0}, owner=AuthenticatedUser "root" ["admin","manager"] (Just "Root user"), participants=[]} - , {title="Advanced Programming", when={DateTime|year=2017,mon=11,day=10,hour=10,min=30,sec=0}, duration={Time|hour=2,min=0,sec=0}, owner=AuthenticatedUser "root" ["admin","manager"] (Just "Root user"), participants=[]} + [ {title="Advanced Programming", when={DateTime|year=2017,mon=11,day=13,hour=10,min=30,sec=0}, duration={Time|hour=2,min=0,sec=0}, owner=AuthenticatedUser "root" ["admin","manager"] (Just "Root user"), participants=[]} + , {title="Information Retrieval", when={DateTime|year=2017,mon=11,day=13,hour=15,min=30,sec=0}, duration={Time|hour=2,min=0,sec=0}, owner=AuthenticatedUser "root" ["admin","manager"] (Just "Root user"), participants=[]} + , {title="Cultural Contacts", when={DateTime|year=2017,mon=11,day=14,hour=13,min= 0,sec=0}, duration={Time|hour=2,min=0,sec=0}, owner=AuthenticatedUser "root" ["admin","manager"] (Just "Root user"), participants=[]} + , {title="Historical Grammar", when={DateTime|year=2017,mon=11,day=14,hour=15,min= 0,sec=0}, duration={Time|hour=2,min=0,sec=0}, owner=AuthenticatedUser "root" ["admin","manager"] (Just "Root user"), participants=[]} + , {title="Text Mining", when={DateTime|year=2017,mon=11,day=15,hour= 8,min=30,sec=0}, duration={Time|hour=2,min=0,sec=0}, owner=AuthenticatedUser "root" ["admin","manager"] (Just "Root user"), participants=[]} + , {title="Testing Techniques", when={DateTime|year=2017,mon=11,day=15,hour= 8,min=30,sec=0}, duration={Time|hour=2,min=0,sec=0}, owner=AuthenticatedUser "root" ["admin","manager"] (Just "Root user"), participants=[]} + , {title="Testing Techniques", when={DateTime|year=2017,mon=11,day=17,hour= 8,min=30,sec=0}, duration={Time|hour=2,min=0,sec=0}, owner=AuthenticatedUser "root" ["admin","manager"] (Just "Root user"), participants=[]} + , {title="Advanced Programming", when={DateTime|year=2017,mon=11,day=17,hour=10,min=30,sec=0}, duration={Time|hour=2,min=0,sec=0}, owner=AuthenticatedUser "root" ["admin","manager"] (Just "Root user"), participants=[]} ] belongsTo :: User Appointment -> Bool @@ -85,8 +88,10 @@ where finishTask :: Task () finishTask = wait (Title "Wait for the start of this appointment") (\now -> now >= app.when) currentDateTime >>= \_ -> - viewInformation (Title app.Appointment.title) [] "Click 'Done' to finish this task." >>* - [OnAction (Action "Done") (always $ return ())] + viewSharedInformation (Title app.Appointment.title) [ViewAs (const "Click 'Done' to finish this task.")] currentDateTime >>* + [ OnAction (Action "Done") (always $ return ()) + , OnValue (ifValue (\now -> now >= addTime app.duration app.when) (const $ return ())) + ] attrs :: User -> TaskAttributes attrs u = workerAttributes u @@ -129,7 +134,8 @@ addProposedAppointment papp=:{appointment={Appointment|title}} = where chooseTask :: Int User -> Task (Int, 'M'.Map Int ProposedAppointment) chooseTask idx user = - allTasks [updateInformation (toString w) [] Maybe \\ (w,_) <- papp.startOptions] >>= \avail -> + viewSharedInformation (Title title) [ViewUsing (fromJust o 'M'.get idx o snd) proposalViewer] proposedAppointments + ||- allTasks [updateInformation (toString w) [] Maybe \\ (w,_) <- papp.startOptions] >>= \avail -> upd (second $ 'M'.alter (fmap (setAvailability user avail)) idx) proposedAppointments where setAvailability :: User [Availability] ProposedAppointment -> ProposedAppointment @@ -156,13 +162,19 @@ where ] ) $> () where - proposalViewer :: Editor ProposedAppointment - proposalViewer = comapEditorValue toTuple $ container4 - (withLabelAttr "Title" gEditor{|*|}) - (withLabelAttr "Duration" gEditor{|*|}) - (withLabelAttr "Participants" usersViewer) - (withLabelAttr "Start options" grid) + schedule start = addAppointment {papp.appointment & when=start} >>| cancel + cancel = + allTasks (map (flip removeTask topLevelTasks) chooseTasks) >>| + upd (second $ 'M'.del idx) proposedAppointments + + proposalViewer :: Editor ProposedAppointment + proposalViewer = comapEditorValue toTuple $ container4 + (withLabelAttr "Title" gEditor{|*|}) + (withLabelAttr "Duration" gEditor{|*|}) + (withLabelAttr "Participants" usersViewer) + (withLabelAttr "Start options" grid) + where toTuple :: ProposedAppointment -> (String, Time, [User], (ChoiceGrid, [Int])) toTuple papp = ( papp.appointment.Appointment.title @@ -179,12 +191,6 @@ where ) ) - schedule start = addAppointment {papp.appointment & when=start} >>| cancel - - cancel = - allTasks (map (flip removeTask topLevelTasks) chooseTasks) >>| - upd (second $ 'M'.del idx) proposedAppointments - manageAttrs :: User -> TaskAttributes manageAttrs u = workerAttributes u [ ("title", "Manage " +++ title) @@ -214,7 +220,7 @@ showAppointments = appointments where editor = listEditor Nothing False False Nothing (bijectEditorValue toTuple fromTuple tupEdit) - tupEdit = toolbar5 gEditor{|*|} gEditor{|*|} gEditor{|*|} userViewer usersViewer + tupEdit = listitem5 gEditor{|*|} gEditor{|*|} gEditor{|*|} userViewer usersViewer <<@ directionAttr Horizontal userViewer = comapEditorValue toString gEditor{|*|} toTuple app = (app.Appointment.title, app.when, app.duration, app.owner, app.participants) fromTuple (t,w,d,o,p) = {title=t, when=w, duration=d, owner=o, participants=p} diff --git a/assignment-7/assignment-7.tex b/assignment-7/assignment-7.tex new file mode 100644 index 0000000..bb0dd17 --- /dev/null +++ b/assignment-7/assignment-7.tex @@ -0,0 +1,117 @@ +\documentclass[a4paper,english]{article} + +\title{Making Appointments\\\Large{Advanced Programming assignment 7}} +\author{Camil Staps} + +\usepackage[margin=25mm,bottom=35mm]{geometry} +\usepackage{graphicx} +\usepackage[hidelinks]{hyperref} +\usepackage{cleveref} +\usepackage{csquotes} +\usepackage{minted} + +\begin{document} + +\maketitle + +\noindent\emph{The screenshots included here are also in higher resolution included in the tarball.} + +\section{Showing appointments} +There are two ways of showing appointments. +All future appointments can be shown in a list (\cref{fig:show-list}). +The appointments for the current week can be shown in an SVG calendar (\cref{fig:show-svg}). +This can of course be extended to see other weeks as well, but it was really just to try out SVG. + +\begin{figure}[b] + \includegraphics[width=\textwidth]{show-list} + \caption{Showing future appointments as a list.\label{fig:show-list}} +\end{figure} +\begin{figure}[b] + \includegraphics[width=\textwidth]{show-svg} + \caption{Showing current week as an SVG calendar.\label{fig:show-svg}} +\end{figure} + +\section{Making appointments} +The default start time is the next whole hour. +Other than that, this part is not much different than the screenshot in the assignment. +It was not clear to me what the \enquote{scheduled appointments} field should do, so I omitted it. +Users are not automatically participants in their own appointment which is the decision that gives most user freedom. +See \cref{fig:make}. + +\begin{figure}[b] + \includegraphics[width=\textwidth]{make} + \caption{Making a new appointment.\label{fig:make}} +\end{figure} + +\section{Proposing appointments} +Proposed appointments are stored in a new type and stored in a separate share. + +\inputminted[firstline=114,lastline=117]{clean}{appointments.icl} +\inputminted[firstline=102,lastline=105]{clean}{appointments.icl} + +The interface for creating an appointment proposal is straightforward (\cref{fig:propose}). +Participants get tasks to indicate their availability (\enquote{Yes}, \enquote{No} or \enquote{Maybe}; \cref{fig:propose-avail}). +The owner can always pick a start time or cancel the appointment (\cref{fig:propose-manage}). +In both cases, the tasks to indicate availability are removed; + in the first case, tasks to finish the appointment are created (see \cref{sec:user-tasks}). + +\begin{figure}[b] + \includegraphics[width=\textwidth]{propose} + \caption{Proposing an appointment.\label{fig:propose}} +\end{figure} +\begin{figure}[b] + \includegraphics[width=\textwidth]{propose-avail} + \caption{Indicating availability for a proposed appointment.\label{fig:propose-avail}} +\end{figure} +\begin{figure}[b] + \includegraphics[width=\textwidth]{propose-manage} + \caption{Managing a proposed appointment.\label{fig:propose-manage}} +\end{figure} + +\section{User tasks} +\label{sec:user-tasks} +User tasks are visible from the moment that they are created, + but can only be finished + (properly; they can always be deleted due to the usage of \texttt{loginAndManageWorkList}) + after the start time (\cref{fig:user-early}). +During the appointment, the user can finish a task by clicking \enquote{Done} (\cref{fig:user-task}). +User tasks disappear after the end time. + +\begin{figure}[b] + \includegraphics[width=\textwidth]{user-early} + \caption{View when the user opens a task which does not start yet.\label{fig:user-early}} +\end{figure} +\begin{figure}[b] + \includegraphics[width=\textwidth]{user-task} + \caption{View to finish a task.\label{fig:user-task}} +\end{figure} + +\section{Overall design \& feedback requests} +I tried to separate model and view/controller. +For instance, there are \texttt{addAppointment :: Appointment -> Task Appointment} and + \texttt{addProposedAppointment :: ProposedAppointment -> Task Int} which do not handle user interaction + but add a (proposed) appointment to the right share and start up related tasks. +The relevant interaction tasks, \texttt{makeAppointment} and \texttt{proposeAppointment}, + are just wrappers around the model tasks. +I think something like this may be possible with lenses, i.e., + change the addition function of the shares to add the user tasks. +I have not tried to find this out, but would like some feedback on that. + +Also, you can see that to update start options users modify the map of availabilities of the proposed appointment. +For this they also need to be able to find the appointment in the list of proposed appointment, + so the proposed appointments share is a tuple of an index for the next proposed appointment and a \texttt{Map Int ProposedAppointment} for the proposed apointments themselves. +I expect that this can be done neater as well, possibly with lenses, so would like feedback on that as well. + +\section{Known bugs} +This program is affected by bug \href{https://gitlab.science.ru.nl/clean-and-itasks/iTasks-SDK/issues/181}{\#181}. + +It was affected by \href{https://gitlab.science.ru.nl/clean-and-itasks/iTasks-SDK/issues/188}{\#188}, + but this has been resolved. So, make sure to run with the latest version (this program has been tested with 2017-11-12). + +Some workarounds were needed for \href{https://gitlab.science.ru.nl/clean-and-itasks/iTasks-SDK/issues/190}{\#190}. +Particularly, that bug made it difficult to do client-side date handling. +This required me to give the \texttt{editor} and \texttt{draw} functions in \texttt{showCalendar} a \texttt{[Date]} argument, + thereby moving the \texttt{take 7 \$ iterate nextDay \$ previous Sunday date} to the server. +I would find it preferable to move that computation to the \texttt{draw} function. + +\end{document} diff --git a/assignment-7/make.png b/assignment-7/make.png Binary files differnew file mode 100644 index 0000000..b30b4d6 --- /dev/null +++ b/assignment-7/make.png diff --git a/assignment-7/propose-avail.png b/assignment-7/propose-avail.png Binary files differnew file mode 100644 index 0000000..5e3ae80 --- /dev/null +++ b/assignment-7/propose-avail.png diff --git a/assignment-7/propose-manage.png b/assignment-7/propose-manage.png Binary files differnew file mode 100644 index 0000000..1bf3421 --- /dev/null +++ b/assignment-7/propose-manage.png diff --git a/assignment-7/propose.png b/assignment-7/propose.png Binary files differnew file mode 100644 index 0000000..7f1f58a --- /dev/null +++ b/assignment-7/propose.png diff --git a/assignment-7/show-list.png b/assignment-7/show-list.png Binary files differnew file mode 100644 index 0000000..c6b93c9 --- /dev/null +++ b/assignment-7/show-list.png diff --git a/assignment-7/show-svg.png b/assignment-7/show-svg.png Binary files differnew file mode 100644 index 0000000..1514a0b --- /dev/null +++ b/assignment-7/show-svg.png diff --git a/assignment-7/user-early.png b/assignment-7/user-early.png Binary files differnew file mode 100644 index 0000000..dfea658 --- /dev/null +++ b/assignment-7/user-early.png diff --git a/assignment-7/user-task.png b/assignment-7/user-task.png Binary files differnew file mode 100644 index 0000000..56c4c5b --- /dev/null +++ b/assignment-7/user-task.png |