Upload
others
View
1
Download
0
Embed Size (px)
Citation preview
More tools for Canvas Realizing a Digital Form with Dynamically Presented Questions and Alternatives
RESHAD SARWAR and NATHAN MANZI
DEGREE PROJECT IN INFORMATION AND COMMUNICATION TECHNOLOGY AND
DEGREE PROJECT IN ELECTRONICS AND COMPUTER ENGINEERING, FIRST
CYCLE
STOCKHOLM, SWEDEN 2019
KTH ROYAL INSTITUTE OF TECHNOLOGY
E le c t r i c a l E n g i n e e r i n g a n d C o m p u t e r S c i e n c e
More tools for Canvas Realizing a Digital Form with
Dynamically Presented
Questions and Alternatives
Reshad Sarwar and Nathan Manzi 2019-05-05
Bachelor’s thesis
Examiner Gerald Q. Maguire Jr.
Academic adviser Anders Västberg
KTH Royal Institute of Technology
School of Electrical Engineering and Computer Science (EECS)
Department of Communication Systems
SE-100 44 Stockholm, Sweden
Abstract | i
Abstract
At KTH, students who want to start their degree project must complete a paper
form called “UT-EXAR: Ansökan om examensarbete/application for degree
project”. The form is used to determine students’ eligibility to start a degree project,
as well as potential examiners for the project. After the form is filled in and signed
by multiple parties, a student can initiate his or her degree project. However, due to
the excessively time-consuming process of completing the form, an alternative
solution was proposed: a survey in the Canvas Learning Management System
(LMS) that replace s the UT-EXAR form.
Although the survey reduces the time required by students to provide
information and find examiners, it is by no means the most efficient solution. The
survey suffers from multiple flaws, such as asking students to answer unnecessary
questions, and for certain questions, presenting students with more alternatives
than necessary. The survey also fails to automatically organize the data collected
from the students’ answers; hence administrators must manually enter the data
into a spreadsheet or other record.
This thesis proposes an optimized solution to the problem by introducing a
dynamic survey. Moreover, this dynamic survey uses the Canvas Representational
State Transfer (REST) API to access students’ program-specific data. Additionally,
this survey can use data provided by students when answering the survey questions
to dynamically construct questions for each individual student as well as using
information from other KTH systems to dynamically construct customized
alternatives for each individual student. This solution effectively prevents the
survey from presenting students with questions and choices that are irrelevant to
their individual case. Furthermore, the proposed solution directly inserts the data
collected from the students into a Canvas Gradebook.
In order to implement and test the proposed solution, a version of the Canvas
LMS was created by virtualizing each Canvas-based microservice inside of a Docker
container and allowing the containers to communicate over a network.
Furthermore, the survey itself used the Learning Tools Interoperability (LTI)
standard. When testing the solution, it was seen that the survey has not only
successfully managed to filter the questions and alternative answers based on the
user’s data, but also showed great potential to be more efficient than a survey with
statically-presented data. The survey effectively automates the insertion of the data
into the gradebook.
Keywords:
Canvas, Learning Management System (LMS), Learning Tools Interoperability
(LTI), Docker containers, Microservices Architecture, REST API, Dynamic survey
systems
Sammanfattning | iii
Sammanfattning
På KTH, studenter som skall påbörja sitt examensarbete måste fylla i en blankett
som kallas “UT-EXAR: Ansökan om examensarbete/application for degree
project”. Blanketten används för att bestämma studenters behörighet för att göra
examensarbete, samt potentiella examinator för projektet. Efter att blanketten är
fylld och undertecknad av flera parter kan en student påbörja sitt examensarbete.
Emellertid, på grund av den alltför tidskrävande processen med att fylla
blanketten, var en alternativ lösning föreslås: en särskild undersökning i Canvas
Lärplattform (eng. Learning Management System(LMS)) som fungerar som
ersättare för UT-EXAR-formulär.
Trots att undersökningen har lyckats minska den tid som krävs av studetenter
för att ge information och hitta examinator, det är inte den mest effektiva
lösningen. Undersökningen lider av flera brister, såsom att få studenterna att svara
på fler frågor än vad som behövs, och för vissa frågor, presenterar studenter med
fler svarsalternativ än nödvändigt. Undersökningen inte heller automatiskt med att
organisera data som samlats in från studenters svar. Som ett resultat skulle en
administratör behöva organisera data manuellt i ett kalkylblad.
Detta examensarbete föreslår en mer optimerad lösning på problemet:
omskrivning av undersökningens funktionaliteter för att använda Representational
State Transfer(REST) API för att komma åt studenters programspecifika data i
back-end, såväl att använda speciella haschar för att hålla referenser till uppgifter
som lämnas av studenterna när de svarar på frågorna i undersökningen, så att
undersökningen inte bara kan använda dessa data för att dynamiskt konstruera
frågor för varje enskild student, men också dynamiskt konstruera svarsalternativ
för varje enskild student. Denna lösning förhindrar effektivt undersökningen från
att presentera studenter med frågor och valbara svarsalternativ som är helt
irrelevanta för var och en av deras individuella fall. Med den föreslagna lösningen
kommer undersökningen dessutom att kunna organisera de data som samlats in
från Studenterna till ett speciellt Canvas-baserat kalkyllblad, kallas som Betygsbok.
För att genomföra och testa den förslagna lösningen skapades en testbar version
av Canvas LMS genom att virtualisera varje Canvas-baserad mikroservice inuti en
dockercontainer och tillåter containers att kommunicera över ett nätverk.
Dessutom var undersökningen själv konfigurerad för att använda Lärverktyg
Interoperability (LTI) standard. Vid testning av lösningen, det visade sig att
undersökningen på ett sätt effektivt har lyckats använda vissa uppgifter från en
testanvändare att bara endast svara på de relevanta frågorna, men också presentera
användaren med en mer kondenserad lista svarsalternativ över baserat på data.
Nyckelord: Canvas, Lärplattform, Lärverktyg Interoperabilitet,
Dockercontainenrar, Mikroservices arkitekektur, RESTful API, Dynamisk
undersökningssystem
Acknowledgments | v
Acknowledgments
We would like to thank Prof. Gerald Q. Maguire for all his help and support.
Stockholm, April 2019
Reshad Sarwar and Nathan Manzi
Table of contents | vii
Table of contents
Abstract ..................................................................................... i Sammanfattning ...................................................................... iii Acknowledgments.................................................................... v
Table of contents ................................................................... vii List of Figures ......................................................................... xi List of Tables ......................................................................... xiii List of Listings ........................................................................ xv
List of acronyms and abbreviations .................................... xix
1 Introduction ......................................................................... 1
1.1 Background .................................................................................. 1 1.2 Problem ........................................................................................ 1 1.3 Purpose ........................................................................................ 3
1.4 Goals ............................................................................................ 4 1.5 Research Methodology ............................................................... 5 1.6 Delimitations ................................................................................ 6 1.7 Structure of the thesis ................................................................ 7
2 Background ....................................................................... 9
2.1 Canvas Learning Management System ..................................... 9
2.2 Canvas API ................................................................................... 9
2.3 Learning Tools Interoperability (LTI) ....................................... 10
2.4 Docker Container ....................................................................... 11 2.4.1 How does a Docker container work? .............................. 12
2.4.2 Implementation of a simple Docker container ................. 13 2.4.3 Further improvement via Volumes .................................. 15 2.4.4 The Docker Compose Tool ............................................. 15
2.4.5 Aliases and Anchors ....................................................... 17
2.5 Microservices Architecture....................................................... 18 2.5.1 Asynchronous Message-based Communication ............. 19 2.5.2 Request-Response Communication ............................... 21
2.6 Reverse proxies ......................................................................... 22
2.7 REST ........................................................................................... 23 2.7.1 Architectural Constraints of REST API ............................ 23 2.7.2 Request Anatomy ........................................................... 23
2.8 Related work .............................................................................. 25
2.8.1 Connecting Silos ............................................................. 25 2.8.2 On-demand laboratory working environments for
Internetworking e-learning .............................................. 25 2.8.3 Using custom columns for metadata data about a
thesis .............................................................................. 26
2.8.4 An LTI application for automatic grading ......................... 26
2.9 Summary .................................................................................... 26
3 Methodology ..................................................................... 27
3.1 Research Process ..................................................................... 27
viii | Table of contents
3.1.1 Identifying research areas ............................................... 27
3.1.2 Literature study ............................................................... 28 3.1.3 Identifying Areas of Implementation for the Dynamic
Quiz Program .................................................................. 28 3.1.4 Identifying Requirements ................................................ 29
3.2 Test Bed ..................................................................................... 30 3.2.1 Virtual Machine ............................................................... 30 3.2.2 Host systems .................................................................. 30
3.3 Development and Testing Process .......................................... 31 3.3.1 A Variant of Test-driven Development ............................ 31
3.3.2 Test case example .......................................................... 32
3.4 Evaluation of the process ......................................................... 34
4 Implementation .............................................................. 35
4.1 Initial setup................................................................................. 35 4.2 Some additional modifications to the system ......................... 40
4.2.1 Changing the overcommit handling mode ....................... 41 4.2.2 Turning off hugepages .................................................... 41 4.2.3 Using a specific Bundler version ..................................... 42 4.2.4 Changing the version of the active_model_serializers
gem ................................................................................. 43 4.2.5 Changing the bullet version ............................................ 43
4.3 A Glimpse into the Interprocess Communication with Wireshark ................................................................................... 43
4.4 The Dynamic Survey ................................................................. 59
4.4.1 Modeling the Dynamic Survey ........................................ 59 4.4.2 Initial Setup Prior to Experimentation .............................. 62
4.4.3 Primary Initializations for REST API calls ....................... 63 4.4.4 Initializations for LTI Integration ...................................... 63 4.4.5 Retrieval a Student’s Program of Study .......................... 65 4.4.6 Retrieving Students’ General Data.................................. 72
4.4.7 Displaying Course Options ............................................. 78 4.4.8 Selecting an Examiner .................................................... 84
4.4.9 Filling in the cells of the gradebook ................................. 86 4.4.10 Modification to the ‘/OutcomeNoExaminer’ route ............ 92
4.5 Embedding the Dynamic Survey into Canvas ......................... 95
5 Analysis ............................................................................ 97
5.1 Results from the Integration Tests .......................................... 97 5.2 Efficiency Evaluation .............................................................. 103 5.3 Validity and Reliability ............................................................ 107
6 Conclusions and Future work ........................................ 109
6.1 Conclusions ............................................................................. 109 6.2 Limitations ............................................................................... 110 6.3 Future work .............................................................................. 110 6.4 Reflections ............................................................................... 111
6.4.1 Social and Economic Aspects ....................................... 111
6.4.2 Ethical Aspects ............................................................. 112
IntroductionTable of contents | 9
References ............................................................................ 113
Appendix A: UT-EXAR form without heading .................... 117
Ansökan om examensarbete, del 1/application for degree project, Part 1 ..................................................... 117
del 2, förutsätter att del 1 har godkänts/ part 2, Requires approved part 1 ............................................... 118
Appendix B: Location of the dynamic survey program ..... 120
List of Figures | xi
List of Figures
Figure 2-1: Launching a Tool Provider (TP) from a Tool Consumer (TC), data taken from [11] .........................................................11
Figure 2-2: The layering of instructions in a Docker image (based on [13]) .......................................................................................... 12
Figure 2-3: Asynchronous message-based communication in a point-to-point channel, data taken from [25] .................................. 20
Figure 2-4: Asynchronous message-based communication in a publish-subscribe channel, data taken from [25] ................... 21
Figure 2-5: Synchronous request/response-based communication via REST, taken from [23] ............................................................. 21
Figure 2-6: Nginx proxy server ................................................................... 22 Figure 3-1: Top-down approach to break the main problem into
multiple sub-problems............................................................. 27 Figure 3-2: TDD sequence .......................................................................... 32 Figure 4-1: Initial output of the 'docker-compose up' command ............. 38 Figure 4-2: Final output of the 'docker-compose up' command................ 39 Figure 4-3: The Canvas log-in page ........................................................... 40 Figure 4-4: Home page of the test course .................................................. 40 Figure 4-5: GET request in the Packet List pane (docker0 interface) ....... 45 Figure 4-6: The details of packet 9 in docker0 interface (docker0
interface) ..................................................................................46 Figure 4-7: No DNS packets during GET request to
'/courses/4/assignments/2' (different capture) .....................46 Figure 4-8: Contents of Packet 9 (docker0 interface) ................................... 47 Figure 4-9: TCP handshake and GET request (br-3a6597a77b3f
interface) .................................................................................. 47 Figure 4-10: Communication before GET request (br-3a6597a77b3f
interface) ................................................................................. 48 Figure 4-11: Communication between, web, redis, and postgres (after
GET request; br-3a6597a77b3f interface) ...............................49 Figure 4-12: Final phase of the conversation (br-3a6597a77b3f
interface) ................................................................................. 50 Figure 4-13: Details of packet 412 (br-3a6597a77b3f interface) ................ 50 Figure 4-14: Initial GET request and handshake phase .............................. 51 Figure 4-15: Communication with the redis and postgres containers ........ 51 Figure 4-16: TCP packet transfer from web to Nginx .................................. 52 Figure 4-17: Connection termination and response ...................................... 52 Figure 4-18: GET request to '/courses/4/modules' (in docker0
interface) .................................................................................. 58 Figure 4-19: Handshake phase and GET request to
'courses/4/modules' ................................................................ 58 Figure 4-20: Response and termination phase ............................................ 59 Figure 4-21: Event flow of the dynamic survey ............................................ 61 Figure 4-22: Setting up an LTI-based external tool in Canvas .................... 95 Figure 4-23: Setting up an assignment to use the LTI-based external
tool ...........................................................................................96
xii | List of Figures
Figure 5-1: First page of the dynamic survey when the user has program data available ............................................................ 97
Figure 5-2: First page of the survey when the user does not have program data available ........................................................... 98
Figure 5-3: Notifying TIVNM students that they must have the A-F grading scale ............................................................................99
Figure 5-4: Allowing non-TIVNM students to select a grading scale ........99 Figure 5-5: Pass/Fail courses selectable for CELTE students ................. 100 Figure 5-6: Pass/Fail courses selectable by TCOMM students ................ 100 Figure 5-7: A-F courses selectable by TNTEM students .......................... 100 Figure 5-8: Presenting examiner options based on selected degree
project course (left shows examiners for the course DA224X, while the right shows examiners for the course IL246X) .................................................................................. 101
Figure 5-9: Final page of the dynamic survey for course: DA224X and examiner: Anders Lansner .................................................... 102
Figure 5-10: Final page of the dynamic survey for a course with no examiner ................................................................................ 102
Figure 5-11: The gradebook after server students have answered the dynamic survey ...................................................................... 102
Figure 5-12: The gradebook tested with SinatraTest20 ............................ 103
List of Tables | xiii
List of Tables
Table 2-1: HTTP methods in a request ..................................................... 24 Table 3-1: Submodules for retrieving student data ................................. 28 Table 3-2: Requirements for integration tests to be successful ............... 29 Table 4-1: IP addresses of all the docker containers ................................44 Table 4-2: Legend for figures 4-14, 4-15, 4-16, and 4-17 .......................... 53 Table 4-3: SIS ID associated with each fake user ..................................... 62 Table 4-4: Some of the requests for Canvas’s custom gradebook
columns API ............................................................................. 87
List of Listings | xv
List of Listings
Listing 2-1: The 'config.json' file ................................................................. 10 Listing 2-2: Content of the PHP file ............................................................ 13 Listing 2-3: The Dockerfile contents ........................................................... 13 Listing 2-4: Command for running the Dockerfile .................................... 14 Listing 2-5: Command for running the hello-world image ........................ 14 Listing 2-6: Command for mounting the src directory as a volume ........... 15 Listing 2-7: Example of a 'docker-compose.yml' file .................................. 16 Listing 2-8: Similar configurations in multiple services ............................. 17 Listing 2-9: Using aliases and anchors........................................................ 18 Listing 2-10: Example of a request endpoint (Github API) .......................... 23 Listing 2-11: Example of a request endpoint (Canvas API) ......................... 23 Listing 3-1: Test-case for filtering courses .................................................. 33 Listing 3-2: Refactored version of the ‘testCINTEcourses’ route ............... 33 Listing 4-1: Authentications required for logging in to the Ubuntu
system in the VM ..................................................................... 35 Listing 4-2: The output in the bash terminal when the 'dory status'
command is executed .............................................................. 36 Listing 4-3: Execution of the 'dory up' command in the Ubuntu
terminal .................................................................................... 36 Listing 4-4: Execution of the 'dig canvas.docker' command in the
Ubuntu terminal ...................................................................... 37 Listing 4-5: Listing all the running containers with the ‘docker-
compose ps’ command ............................................................. 39 Listing 4-6: Setting overcommit handling mode to 1 ................................. 41 Listing 4-7: Turning off hugepages ............................................................. 41 Listing 4-8: Making the '/etc/rc.local' file executable ................................ 42 Listing 4-9: Ensuring that the '/etc/rc.local' file is executed ...................... 42 Listing 4-10: Using a specific Bundler version ............................................. 42 Listing 4-11: Changes to the 'app.rb' file ...................................................... 43 Listing 4-12: Changes to the 'assets.rb' file ................................................... 43 Listing 4-13: The bridge networks ................................................................ 43 Listing 4-14: IP addresses of the bridge networks ........................................44 Listing 4-15: Partial data from packet 417 .................................................... 55 Listing 4-16: Dynamic survey represented in DOT ..................................... 60 Listing 4-17: Primary initializations made for REST API calls .................... 63 Listing 4-18: Variables initialized for LTI-specific functionalities ...............64 Listing 4-19: Configurations made to ensure the tool follows the LTI
standard ...................................................................................64 Listing 4-20: The namespace parameter stored in the body of the GET
request ......................................................................................66 Listing 4-21: Test to ensure SIS ID is linked to the user ..............................66 Listing 4-22: URL for GET request ...............................................................66 Listing 4-23: The ‘@getResponse’ variable ...................................................66 Listing 4-24: Test to check if the user is part of a study program ................66 Listing 4-25: Checking if the user is taking one or more study programs ... 67 Listing 4-26: The /getUserProgram route ................................................... 68
xvi | List of Listings
Listing 4-27: The programs_in_the_school_with_titles variable ...............69 Listing 4-28: Format of each “program” in
programs_in_the_school_with_titles ....................................69 Listing 4-29: Initializing the global variable
'$programs_in_the_school_with_titles' ................................69 Listing 4-30: The 'programs_in_cycle' helper function ............................... 70 Listing 4-31: The /gotUsersProgram route ................................................... 72 Listing 4-32: Retrieval of user’s program code ............................................. 72 Listing 4-33: Checking study cycle and initializing minimum and
maximum possible start dates for the degree project ............. 72 Listing 4-34: Initializing the contents of the
‘@graded_or_ungraded_question’ variable depending on the user’s program ................................................................... 73
Listing 4-35: Initializing the contents of the ‘@graded_or_ungraded_question’ variable depending on the user’s program ................................................................... 74
Listing 4-36: HTML form (code) for obtaining general information from the user ..................................................................................... 75
Listing 4-37: Retrieving and saving the general data entered by the student ..................................................................................... 77
Listing 4-38: Initializing the @grading_scale variable ................................. 78 Listing 4-39: Redirect user to new route depending on the value of
@grading_scale ....................................................................... 78 Listing 4-40: Required initializations that the “/grading_scale_AF” and
“grading_scale_PF” routes depend on .................................... 78 Listing 4-41: Format of each value in relevant_courses_English ................ 79 Listing 4-42: Format of each value in relevant_courses_Swedish ............... 79 Listing 4-43: First section of the ‘/grading_scale_AF’ route ...................... 80 Listing 4-44: The ‘filter_courses_for_a_program’ function ....................... 80 Listing 4-45: The second section of the ‘/grading_scale_AF’ route ............. 81 Listing 4-46: Final section of the ‘/grading_scale_AF’ route ...................... 82 Listing 4-47: The ‘/grading_scale_PF’ route ............................................... 83 Listing 4-48: Retrieving a list of all examiners ........................................... 84 Listing 4-49: Format of each value in the 'all_course_examiners' object ... 84 Listing 4-50: First section the of ‘/Examiner’ route .................................... 84 Listing 4-51: Second section of the ‘/Examiner’ route ................................. 85 Listing 4-52: The ‘/OutcomeNoExaminer’ route ......................................... 86 Listing 4-53: Format of the ‘CustomColumn’ object, data taken from
[49] ........................................................................................... 87 Listing 4-54: The format of the ‘ColumnDatum’ object ................................ 87 Listing 4-55: ‘list_custom_columns’ function (showing the essential
code) ........................................................................................ 88 Listing 4-56: ‘lookup_column_number' function (showing the essential
code) ........................................................................................ 88 Listing 4-57: ‘put_custom_column_entries_by_name’ function
(showing only the essential code) ........................................... 89 Listing 4-58: First section of the ‘/Outcome’ route (edited to show only
the essential code) ................................................................... 89 Listing 4-59: The second section of the ‘/Outcome’ route ........................... 90
IntroductionList of Listings | 17
Listing 4-60: Third section of the ‘/Outcome’ route (edited to show only the essential code) ................................................................... 90
Listing 4-61: Final section of the ‘/Outcome’ route ...................................... 92 Listing 4-62: Modified version of the '/OutcomeNoExaminer' route........... 93 Listing 5-1: Course codes for CELTE for cycle 2 and P/F grading scale
in the reference file ................................................................ 100 Listing 5-2: Course codes for TCOMM for cycle 2 and P/F grading
scale in the reference file ....................................................... 100 Listing 5-3: Course codes for TNTEM for cycle 2 and A-F grading scale
in the reference file ................................................................ 101 Listing 5-4: Sample route created to collect numerical data regarding
courses and programs ............................................................ 103 Listing 5-5: Output from the 'CourseProgramStatistics' route ................ 105 Listing 5-6: Sample route created to collect numerical data regarding
the examiners ......................................................................... 105 Listing 5-7: Output from the 'getExaminerStatistics' route ..................... 106
List of acronyms and abbreviations | xix
List of acronyms and abbreviations
AMQP Advanced Message Queuing Protocol
API Application Programming Interface
CPU Central Processing Unit
CRUD CREATE, READ, UPDATE, DELETE
DiVA Digitala Vetenskapliga Arkivet
XP Extreme Programming
GDPR General Data Protection Regulation
HTTP Hypertext Transfer Protocol
IPC Interprocess Communication
IPv4 Internet Protocol version 4
JSON Javascript Object Notation
KTH Kungliga Tekniska Högskolan
LADOK Lokalt ADB–baserat DOKumentationssystem
LDAP Lightweight Directory Access Protocol
LIS Learning Information System
LMS Learning Management System
LTI Learning Tools Interoperability
MAC Media Access Control
NAT Network Address Translation
OHCI Open Host Controller Interface
OS Operating System
PHP Hypertext Preprocessor
RAM Random Access Memory
REST Representational State Transfer
SIS Student Information System
STOMP Streaming Text Oriented Message Protocol
TC Tool Consumer
TCP Transmission Control Protocol
TDD Test-driven Development
TP Tool Provider
VM Virtual Machine
XML Extensible Markup Language
Introduction | 1
1 Introduction
This chapter provides an overview of the problem addressed in this thesis, some
contextual information regarding the problem and the objectives of this thesis
project, as well as the overall structure of this thesis.
In the modern age, educational institutions are driven by digitalization. As a
result, people utilize electronic forms, rather than paper forms, to collect
information from students, faculty, and staff. This transformation paves the way to
more sustainable operations within educational institutions and other
organizations. The solution proposed in this thesis project aims to contribute to
this digitalization.
1.1 Background
Each year at KTH Royal Institute of Technology (KTH), over 2000 students will
start a degree project. Before they can start this project most students fill out the
paper form “UT-EXAR: Ansökan om examensarbete/application for degree
project”*. The purpose of this document is to obtain information to determine if a
student is eligible to start a 1st or 2nd cycle degree project. Furthermore, it is also
used to determine if the student’s project proposal is suitable for their degree and
to identify a suitable examiner for the project. The form has different sections
which are to be filled in by different parties in the university. One section is filled in
by the student, another by the potential examiner, and another by the school’s
administration. When the form is accepted, the student is registered in the central
student record system called LADOK†.
KTH has adopted the Instructure, Inc. Canvas Learning Management System
(LMS) as the main means for communication between students and teachers in the
context of courses. Professor Gerald Q. Maguire Jr. proposed the use of Canvas
courses to manage students’ degree projects. In this proposed method, all of the
activities associated with a degree project are managed via a Canvas course –
ideally from start to finish. This process assumes that students who are eligible to
start a thesis project are registered in the correct Canvas course.
An earlier degree project (Connecting Silos by students Shiva Besharat Pour and
Qi Li) [2] showed how to take the final thesis as submitted via Canvas and
automate the reporting of the final thesis in the university’s digital archiving system
and how one could potentially automate the announcement of the student’s oral
presentation of their thesis project from the draft that is submitted via Canvas for
the opponent.
1.2 Problem
Although the usage of the UT-EXAR form has persisted for many years, the overall
process has a number of flaws. For example, it does not exploit the opportunities of
digitalization, leading to several bottlenecks in a student’s preparations for a degree
* The UT-EXAR form without heading is shown in Appendix A.
† Lokalt ADB–baserat DOKumentationssystem [1]
2 | Introduction
project. The amount of time it takes to collect all the required signatures, as well as
and the lack of online access to the collected information leads to complications in
registering students for their degree projects (which results in a loss of income for
both the department and the university). Furthermore, it increases the delay for
students to complete their degree project. In the worst case, administrators fail to
find an examiner for a student’s degree project or the student is found to be
ineligible for a degree and the student is never informed of these difficulties,
leading to additional bottlenecks in the process.
One of the solutions proposed by Prof. Gerald Maguire is the collection of
students’ information for their degree project by using a survey, instead of relying
on the UT-EXAR form for the same task. The survey acts as a direct replacement
for the UT-EXAR form. This survey is static, hence all the questions and alternative
choices in the survey are fixed regardless of who is participating in the survey.
Students provide all the information regarding their degree project via answers to
questions in the survey. This information includes their choice of degree project
course and their choice of examiner. Some students may need to provide a large
amount of information via the survey, while other students may only need to
provide a small amount of information. Unfortunately, with a static survey, both
types of students have to answer all of the questions. In the end, an administrator
will review the collected information and assign an appropriate examiner for each
student and register the student in LADOK for the appropriate course (designated
by a course code) for their degree project.
Although the survey is a major improvement over the UT-EXAR form, its static
nature is a major design flaw which leads to many problems and poor efficiency.
One of the problems with the survey in its current form is that it does not
automatically identify the correct course code based on the program of study for
each student participating in the survey. This occurs because the survey does not
have any logic to utilize the answers that students gave to previous questions. As a
result, students participating in the survey are presented with many questions
which are unnecessary or offered irrelevant choices (the latter leads to students
having to read through a large list of alternative choices although most of these
choices are not relevant to them).
For instance, when 2nd cycle students from the Electrical Engineering program
participate in the survey, they might be presented with a question that asks
whether they wish to have their thesis graded using the Pass-Fail grading scheme
or the A-F grading scheme. Later, the same students will be presented with a
question that asks about which course code they wish to register for (from a long
list of course codes that covers all of the different programs offered within the
school). The process of reading through this large list of courses to identify the
correct answer choice is very time-consuming and is likely to lead to an incorrect
choice. Moreover, these students should only be presented with the two relevant
course codes: IL226X and IL246X (as these are the only degree-project course
options for 2nd cycle students in the Electrical Engineering program). Offering only
Introduction | 3
these two alternatives would make it less likely that the student will make an
incorrect choice. Additionally, given the answer to the question of which grading
scale the student wishes, these students should not have to be asked which course
code to register for since one course code is for the Pass-Fail grading scheme and
the other for the A-F grading scheme.
Another flaw with the proposed survey system is that it is incapable of
determining which examiners are suitable for the degree project course selected by
the student.
Hence, there is a strong need for a system that utilizes students’ answers to
questions, thus tailoring both which questions are asked and reducing the set of
alternatives to only those that are relevant. As described above, in many cases this
would completely eliminate the need for the student to make a choice from a long
list of alternatives.
A possible solution to the problem is to implement a survey that dynamically
constructs questions based on answers provided by the student to previously-asked
questions in the survey. To elaborate, every time a student answers a question in
the survey, the program not only saves the answer but also uses this answer to
determine the subsequent questions that actually need to be asked. Additionally,
these answer can be used to tailor the list of potential alternatives for subsequent
questions. Furthermore, the dynamic survey uses the Canvas Application
Programming Interface (API) to retrieve program-specific information, which can
also be used to determine which questions to present to the student. As a result, all
the questions presented in the survey will be those relevant to the specific student
and for every question presented in the survey, all the alternatives will be only
those relevant to the specific student.
One benefit of this new solution is that students would no longer have to face a
large list of degree project courses to choose from among, where a large percentage
of the courses are not relevant to their program of study. The system instead
presents the students with only the courses which meet the following two criteria:
1. Available for the student’s study program
2. Follows the grading scale selected by the student*
An additional benefit of this solution is that when a student selects a degree
project course to register for, the system presents the student with a list of
examiners who are suitable for the degree project course that was selected by the
student.
1.3 Purpose
The purpose of this degree project is to help students, faculty, and staff facilitate
the administration and progress of degree projects by exploiting the opportunities
of the Canvas LMS. Since the Canvas LMS is already widely used within the
* Note that in some programs the grading scale is restricted to one alternative, hence for a student in such a program – there is no need to ask about their choice of grading scale.
4 | Introduction
university to manage students’ educational activities, it is safe to assume that the
students, faculty, and staff at KTH are competent to use it.
Due to the large number of students doing degree projects each year, it is
important to optimize the entire process of degree projects. Professor Maguire has
built many tools to be used with the Canvas LMS to facilitate students’ degree
projects. Additionally, a number of projects have attempted to exploit the Canvas
LMS to better managing students and their degree projects. This thesis aims to
serve the same general purpose but focuses on the start of this project.
Due to the large number of students applying to do a degree project the
cumulative time to process the paper version of the application forms is very large
(as it takes approximately 30 minutes per form just to input the data that the
student has entered). While the static survey reduces the time spent by the students
and administrators, it is by no means, the optimal solution. Hence, it is desirable to
seek ways in which this process can be further optimized. The static survey was the
first proposed solution to the problem of the paper application forms; hence, the
proposed dynamic survey evolved from this survey.
A large number of students who plan to start their degree project are already
occupied with other courses, thus if the process of finding an appropriate project,
preparing for this project, finding a suitable examiner, and registering for proposal
is too time-consuming, they will waste a lot of time that could otherwise have been
used for their current courses. Hence, creating a more efficient means of collecting
students’ information so that they can start their degree project will allow students
to use their time more efficiently.
It should be noted that the ethical aspects of the information retrieval process
must also be kept in mind when working on such a project. The General Data
Protection Regulation (GDPR), created by the European Union (EU) serves to
ensure that all individuals’ private information is protected. Hence, during the
implementation phase of this project, GDPR-compliance must be ensured [3].
1.4 Goals
To be considered “successful”, the system to be developed over the course of the
project must meet the following objectives:
1. The system must collect only relevant information from a student who plans on
starting his/her degree project (i.e. program of study if not provided by the
system, general information regarding degree project including grading scale
(which can be preset based on program of study), degree-project course based
on program of study, grading scale and student’s choice, and examiner based on
selected degree-project course and student’s choice, if any examiners are
available).
2. The system must reduce the workload on students, faculty, and staff.
3. The system ideally should facilitate assigning an appropriate examiner and
supervisor to a student, based on the student’s project proposal. If it is not
Introduction | 5
possible to do so, the system must help to get the student to a state where this
assignment will be possible.
4. Furthermore, the overall process must achieve a high level of efficiency (with
respect to human efforts – at the cost of more work by the software).
Furthermore, this thesis aims to meet the following objectives:
1. The proposed method should utilize a Representational State Transfer (REST)
API interface to access students’ information from Canvas and/or use special
data structures to store the student’s answers to survey questions.
2. Describe the test environment needed to produce the prototype – so as to
facilitate other students, faculty, and staff being able to create similar tools using
Canvas.
3. Describe how the information collected by the survey can be used to
dynamically construct questions
1.5 Research Methodology
The research methodology that will be relied on during this project is qualitative
research. Meeting the objectives of the project mean implementing a wide range of
functions, which in turn should be implemented by a wide range of tools. Hence,
the goal of the research phase is to identify these tools. This means that during the
research phase, the team will need to find answers to a variety of questions, such as
the questions below:
1. What kind of architectural style can be used to retrieve program-specific
information for each student?
2. What programming languages support the architectural style identified in point
(1)? Do these programming languages provide the built-in functions necessary
to implement many other functions of the final system? How do these built-in
functions behave and how should they be used?
3. What kind of software is capable of virtualizing the Canvas LMS environment
and how do the different components of these software behave?
4. What standards were/are used by earlier students when implementing similar
functions in their degree projects?
As one can see, the research mostly involves identifying different tools and their
corresponding behaviors in order to understand how the project can be
implemented. It does not rely on any mathematical or statistical data, but rather on
tools/languages that can effectively get the job done. This makes other research
methodologies, such as quantitative research, unsuitable for this project.
With regards to implementation, some of the software development practices
relating to Extreme Programming (XP), such as Test-driven Development
(TDD) [4] or similar variants will be integrated into the testing phase of the project.
This will pave the way to applying a testing methodology known as ‘glass-box’
testing. This testing method requires deep knowledge of the internal structure of
6 | Introduction
the system. During the testing process, the code will be thoroughly analyzed for
correctness and output will be compared with the expected output when the code is
executed [5]. This testing methodology will allow us to both validate and verify the
overall system.
1.6 Delimitations
The final technical phase of this degree project is to test the final system and prove
that the system is functional. This project does not aim to create a production-
quality version of the final system, but rather it aims to thoroughly delineate the
concepts that would underlay the implementation of a production-version of the
system. As a result, all the tests run during the course of this project will be done by
the team of two students writing this thesis, together with their examiner and
adviser. Testing with actual users is outside the scope of this 1st cycle degree
project.
Furthermore, due to the lack of a working interface between Ladok and Canvas
at the time of writing, it will be assumed that the process of determining if a
student who has self-enrolled into a course is actually eligible to be a student of that
course is done manually be a member of the staff of the school’s Education office.
The technical component of this project has three parts:
1. Virtualizing a Canvas LMS for testing,
2. Building a dynamic survey system, and
3. Invoking the dynamic survey system from the Canvas LMS via the LTI
interface.
Although there are many different solutions for virtualizing the Canvas LMS for
testing, this project only considers the use of Docker containers. This choice was
made due to the many advantages of using a Docker container for virtualization
(see Chapter 2) and an existing implementation of this form of a Canvas LMS.
Likewise, the task of invoking the dynamic survey from the Canvas LMS could
be achieved with multiple solutions. However, the only solution addressed in this
project is the use of the LTI interface. The main reasons for this are that Canvas
supports the use of LTI for invoking external code and the KTH Information
Technology unit indicated that they preferred extensions via LTI*. Additionally, LTI
is supported by many other LMSs (such as Blackboard and Moodle); hence, the
proposed solution could be used at other universities besides those that use the
Canvas LMS. Moreover, this project should help others to implement dynamic
surveys and quizzes for other purposes.
Lastly, investigating how many students can participate in a dynamic survey at
any given time is outside the scope of this project. Therefore, this project will not
focus on the scaling of the solution, but rather will leave this as future work.
* Note that a production version is expected to be developed either by this IT unit or an external developer.
Introduction | 7
1.7 Structure of the thesis
The thesis begins, in Chapter 2, by providing the reader with an in-depth
understanding of many of the key concepts that are utilized during the project.
Chapter 3 addresses the research methodology initially described in Section 1.5 in
more detail. Chapter 4 describes in more detail how the concepts from Chapter 2
were used for the implementation of the proposed dynamic survey system. Chapter
5 provides the reader with an analysis of the results that were observed during and
after the implementation phase in Chapter 4. Chapter 6 contains some concluding
remarks about the project, as well as suggestions for future work that could be
derived from this project.
Background | 9
2 Background
This chapter introduces some of the key concepts that are vital to the project. It
provides an in-depth understanding of these concepts as well as what roles they
play in the implementation of the dynamic survey system.
2.1 Canvas Learning Management System
Canvas is an application developed by Instructure.com* that provides course
instructors with many tools for administering courses. Such an application is
known as a Learning Management System (LMS). The functions provided by LMSs
can be used by students to submit assignments, participate in course-specific
forums and surveys, and take online quizzes & tests. The type of content that can be
managed in the Canvas LMS is very diverse, ranging from documents, links, videos,
to external applications [7]. It was built via Ruby on Rails, a server-side web
application framework. The system is open-source and the full source code is
available in Github†.
An instructor can use the Canvas LMS to provide students with various forms of
content for the course he/she is responsible for, such as lecture slides, video clips,
copies of past exams of the course, links to external resources that students can use
as study materials for the course, etc. Another advantage of an LMS is that
instructors no longer have to worry about making hard-copies of their course
material for students to use as study material. Furthermore, an instructor can
provide material that acts as a requirement for a student to complete the course.
This material can be embedded in the Canvas course; for example, assignments,
quizzes, and online labs. Canvas allows an instructor to easily manage submission
deadlines for student submissions. An instructor may create a forum within their
course in Canvas that allows the teacher (and even other students) to communicate
and assist students with useful information. Students can use functions in a Canvas
course to explicitly follow and manage their progress in a course (for example, the
syllabus, “to do”, “upcoming”, and gradebook functions). Additionally, the Canvas
LMS also provides extensive data analytics to the university; however, details of
this are outside the scope of this thesis.
2.2 Canvas API
The Canvas LMS provides a RESTful API that allows developers to access and
modify data via external applications. This API allows the implementation of
functionalities such as uploading files, accessing courses that a student is registered
to, creating quizzes, creating a gradebook, and creating polls [8].
When creating a program that will use the Canvas API, the program must have a
valid access token and the name of the Canvas system. In this thesis project, both of
these are obtained from a ‘config.json’ file. Listing 2-1 shows the contents of such
a file. The access token and the host name are placed between quotation marks.
* Instructure, Inc. is an educational technology company based in Salt Lake City, Utah [6].
† https://github.com/instructure/canvas-lms
10 | Background
Instructions for generating an access token can be found in
https://kth.instructure.com/courses/11/pages/getting-an-access-token. For
example, one can access the production instance of Canvas that is used at KTH by
setting the host to kth.instructure.com. Similarly, there is a test instance of
Canvas at the host name “kth.test.instructure.com”. Both these instances and
a “kth.beta.instructure.com” instance (for beta testing) are provided by
Instructure.com as software-as-a-service in the cloud.
Listing 2-1: The 'config.json' file
{
"canvas": {
"access_token": "",
"host": ""
}
}
Note that when a user creates a token, this token will give the program the same
access rights in Canvas as this user would have when interacting with Canvas using
the Canvas graphical user interface.
2.3 Learning Tools Interoperability (LTI)
Today, thanks to advances in the field of application development, there is an ever-
increasing number of web-based applications and software that enhance teaching
and learning experiences for instructors and students respectively. Examples of
such applications include virtual classrooms and domain-specific engines for
subjects, such as mathematics and history [9].
Furthermore, due to the increasing popularity of LMSs, there was a growing
need for integrating these educational applications with LMSs. This is where the
IMS Global Learning Consortium Tools Interoperability (LTI) specification comes
in. The goal of the LTI specification is to standardize the integration of any LMS
with any external learning application [9].
LTI specifies the communication protocol between a Tool Consumer (TC) and a
Tool Provider (TP). A TC is basically any system that offers access to a learning
tool. For example, an LMS can be a TC. However, a learning tool could also be
launched from other services, such as Facebook or Google, then Facebook and
Google would be considered TCs [9].
A TP is the software that presents the user with the interfaces of one or more
learning tools. In simple terms, the TP represents the entire learning tool, including
interfaces of the learning tool [9]. The communication between a TC and TP is
illustrated in Figure 2-1.
As shown in Figure 2-1, the user requests an assignment from the TC (which in
this case is the LMS). This prompts the LMS to launch an external tool (together
Background | 11
with arguments) that was configured for this assignment to be invoked via LTI. If
the arguments are verified, then the LTI launch request from the TC is
authenticated and accepted by the TP. This initiates a session for the user who
requested the assignment, which in turn allows the user to interact with the
assignment as realized by the TP [10].
In many cases, a TP may require additional course-specific or student-specific
values in order to be fully functional. Examples of such values are course code,
program code, information about the user’s group, etc. The TP retrieves these
additional parameters either from the TC or from a third-party system. These
additional parameters, as well as the standardized way the TP acquires them are
known as IMS Learning Information Services (LIS) [10].
Figure 2-1: Launching a Tool Provider (TP) from a Tool Consumer (TC), data taken from
[11]
2.4 Docker Container
Before learning more about Docker containers, it is important to first understand
the basic concepts of a Virtual Machine (VM). A VM emulates a given system. This
VM can be running on top of an operating system (OS) or on the bare metal. For
each OS that is being simulated, a VM must be loaded with an OS kernel. This OS
kernel has complete control over everything in the emulated machine, i.e., it has
virtual device drivers and a memory management system for the emulated
machine. Each VM is run on top of a hypervisor (also known as a Virtual Machine
Monitor (VMM)). The hypervisor is implemented as software, hardware, or
firmware that builds and executes a VM. However, the overall virtualization
process is very resource-intensive for the host-machine [12].
12 | Background
Unlike a VM, a Docker container does not need to be loaded on top of the OS
kernel but rather Docker uses containers to provide separate namespaces in the
underlying host machine’s kernel to emulate a separate virtual system. In other
words, the kernel of the host machine is shared by all of the containers.
Furthermore, there is no need for a hypervisor between the host machine and a
VM. As a result, fewer resources are required for containers as compared with VMs.
This implies that there is less overhead on the host machine. Hence, a Docker
container can be created and launched in seconds, whereas creating and launching
a VM can take minutes [13]. Due to these advantages, as of 2018, 3.5 million
applications have been containerized with Docker and the number of downloads for
containerized applications has exceeded 37 billion. This large growth and
popularity of docker are among the reasons it is being used for this project.
2.4.1 How does a Docker container work?
A Docker container runs an instance of a Docker image. Each docker image consists
of a series of read-only layers, where each layer represents an instruction in
“Dockerfile” of that image. In other words, a Docker image has an OS, software,
and application code for the specific container amalgamated (as a layered set of
files) inside a single file, known as a Dockerfile. Every layer in the image contains
read-only state, except for the last layer [14]. An illustrated of this layered
architecture is shown in Figure 2-2.
Figure 2-2: The layering of instructions in a Docker image (based on [13])
Figure 2-2 assumes that the Dockerfile for the image was created by 4 command
lines. Any change made to the running container (such as creating a new file,
appending to an existing file, removing a file, etc.) is written to the container layer.
An advantage of using Docker is that Docker containers are very portable. If a
developer successfully runs a Docker image in a certain environment and wishes to
Background | 13
run the same image in a different environment, he/she does not have to worry
about altering any code in the image; they simply run the image in a new
environment. In simple terms, if a Docker image works on one computer, it will
work on every computer [15].
Another advantage of using Docker containers is that Docker implements a
security mechanism known as sandboxing in order to ensure that applications
running in one container are completely isolated from applications running in
another container. As a result, system failures originating in one container will
have no effect on the applications running in another container [16].
2.4.2 Implementation of a simple Docker container
This section outlines the implementation of a simple docker container and is
inspired by the tutorial outlined by Jake Wright, a London-based web
developer [17]. As mentioned in the previous section, the way to create a container
is as follows:
1. Write a Dockerfile,
2. Build the Dockerfile to create a Docker image, and
3. Run the image to get a container
This section presents an example of the creation of a very simple Docker
container. The goal in this example is to create a simple “Hello World” program
written in PHP and execute the file inside of a container. The content of the PHP
file is shown in Listing 2-2.
Listing 2-2: Content of the PHP file
<?php
echo “Hello, World”;
This program is saved in a directory of the programmer’s choice under a name
of the programmer’s choice. For this example, the program is saved in a directory
called src which in turn is saved in a directory called Docker (which is placed in the
user’s Desktop directory). The program is saved as index.php.
As of now, it is impossible to execute this file without a web server. Hence,
Docker will be used to create a web server.
The next step is to create a Dockerfile. This Dockerfile will be a text file which
will be saved in the Docker directory under the name Dockerfile. This Dockerfile
has the contents shown in Listing 2-3.
Listing 2-3: The Dockerfile contents
FROM php:7.0-apache
COPY src/ /var/www/html
EXPOSE 80
14 | Background
The Dockerfile contains code that configures the container environment. For
example, in this example, we require an OS with PHP and an Apache web server
installed. Typically, one would require another image with all the correct
configurations in order to provide such an OS. However, for this example, it is
sufficient to utilize an existing image (from the Docker Hub) that already contains
both PHP and Apache. Docker Hub is a cloud-based registry service that stores pre-
built container images. The FROM php:7.0-apache line defines the base image for
the container that will be created in this example. In this line, php represents the
name of the image and 7.0-apache represents the variant of the base image that will
be used for this example (these variants are also known as ‘tags’). It should be
noted that several different tags of the same image may exist in the Docker Hub.
However, the tag used in this example is the 7.0-apache tag for the php image.
The second line of the Dockerfile copies the contents of the desired file (which is
inside the src directory) to the php image. Note that any other files in the src
directory will also be copied.
The last line of the Dockerfile exposes TCP port 80. This means when the image
that results from this Dockerfile is executed, it will realize a container that listens
on port 80.
After navigating to the directory that contains the Dockerfile one runs the
command shown in Listing 2-4 to build the Dockerfile.
Listing 2-4: Command for running the Dockerfile
docker build -t hello-world .
In the command line above, hello-world represents the name of the image that
will be created, while the parameter (in this case “.”) that follows represents the
location of the Dockerfile. Assuming the one has navigated to the directory
containing the Dockerfile, a dot to represents the current directory.
Once the Dockerfile is built, all the layers of the image will be compiled, creating
the hello-world image. The next step is to simply run this hello-world image with
the command shown in Listing 2-5.
Listing 2-5: Command for running the hello-world image
docker run -p 80:80 hello-world
The “80:80” of the line above indicates that requests will be forwarded from
port 80 on the host to port 80 of the container.
After execution of the command line above, the container will be created and
can be accessed by navigating to localhost in any web browser (i.e., entering
http://localhost into the web browser as port 80 is the default HTTP port). The
result should be “Hello, World” on the displayed webpage.
Background | 15
2.4.3 Further improvement via Volumes
Although the index.php file has successfully been executed inside of a container,
another problem remains. If one were to alter the code in the PHP file so that the
file has a different output instead of “Hello, World”, one would have to rebuild the
Dockerfile and re-run the resulting image. This overall process may be time-
consuming to many developers.
An alternative solution is to use “volumes”. In short, volumes are the preferred
mechanism for persistent data that are both generated by and used by Docker
containers. Volumes allow for mounting of a local directory inside of a container.
An advantage of a volume is that the image file (resulting from the Dockerfile)
never needs to be altered and the size of the container using the volume is not
increased.
In order to mount the src directory into the container as a volume, the
command line in Listing 2-6 should be executed.
Listing 2-6: Command for mounting the src directory as a volume
docker run -p 80:80 -v /Users/Username/Desktop/Docker/src/:/var/www/html hello-world
Running the command above causes the hello-world image to mount the
/Users/Username/Desktop/Docker/src/ directory* into /var/www/html inside the
container. As a result, any change made to the ‘index.php’ file will be visible inside
the container.
2.4.4 The Docker Compose Tool
It would be impractical for a large application to consist of only one service. Large
applications typically depend on communication between many services, some of
which could be databases and bundlers. When creating such an application using
Docker containers, where each Docker container encapsulates a service, building
and running each container separately to realize the complete system would be a
very time-consuming process. This is where the Docker Compose tool comes into
play.
With the Docker Compose tool, one can define and run applications consisting
of multiple Docker containers. This requires the configuration of the services of the
application inside of a file that uses the data serialization language YAML, which in
turn has a ‘.yml’ extension. The services, as configured by the YAML file, can then
be built and run with a single command [18].
There are three main steps to the process of building and running a multi-
container Docker application [18]:
1) Create a Dockerfile to configure the application’s environment
* Note that this path to the local directory would be typical when running on a Microsoft Windows machine the local directory path could be /home/username/Docker/src on a linux machine.
16 | Background
2) Configure the application’s services in a file called docker-compose.yml.
3) Run the command ‘docker-compose up’ to run the application with the
Compose tool.
Listing 2-7 shows what a docker-compose.yml file might look like (code retrieved
from the official Docker documentation).
Listing 2-7: Example of a 'docker-compose.yml' file
#Source: https://docs.docker.com/compose/overview/
version: '3'
services:
web:
build: .
ports:
- "9090:80"
volumes:
- .:/code
- logvolume01:/var/log
links:
- redis
redis:
image: redis
volumes:
logvolume01: {}
It is seen that application using the docker-compose file in Listing 2-7 consists
of two services: ‘web’ and ‘redis’. The ‘web’ service is configured with the file it
should build, the port number on which it should run, and the volumes it should
use. It is also configured with an extra alias under ‘links’ that it can use to
communicate with the ‘redis’ service. Note that links are not required to enable the
‘web’ service to communicate with the ‘redis’ service. The ‘web’ service can still
communicate with the ‘redis’ service without the use of links, however, it would
have to reach the service by using the actual service name instead of by the alias
[19, 20]. The ‘redis’ service is configured to simply use the redis image from the
docker-hub.
Background | 17
2.4.5 Aliases and Anchors
When writing a docker-compose.yml file for an application, a situation may arise
where multiple services share the same configurations. Such a scenario is shown in
Listing 2-8.
Listing 2-8 shows two services, ‘nodeinfo’ and ‘echoit’, having common
configurations for the ‘labels’, ‘gateway’, and ‘environment’ options. Repeating
configurations for multiple services can be a time-consuming process and can lead
to errors. This problem can be solved with the use of aliases (identified by the ‘*’
character) and anchors (identified by the ‘&’ character). Using anchors, one can
map all the key-value pairs of a service into a special variable. These mappings can
then be merged with another service with the use of aliases [21]. This is shown in
Listing 2-9.
Listing 2-8: Similar configurations in multiple services
#Source: https://medium.com/@kinghuang/docker-compose-anchors-aliases-extensions-a1e4105d70bd
services:
nodeinfo:
labels:
function: “true”
depends_on:
- gateway
environment:
no_proxy: “gateway”
http_proxy: $https_proxy
echoit:
labels:
function: “true”
depends_on:
- gateway
environment:
fprocess: “cat”
no_proxy: “gateway”
http_proxy: $https_proxy
18 | Background
Listing 2-9: Using aliases and anchors
#Source: https://medium.com/@kinghuang/docker-compose-anchors-aliases-extensions-a1e4105d70bd
services:
nodeinfo: &mapping
labels:
function: “true”
environment:
no_proxy: “gateway”
http_proxy: $https_proxy
echoit:
<<: *mapping
environment:
fprocess: “cat”
no_proxy: “gateway”
http_proxy: $https_proxy
In Listing 2-9, all the key-value pairs from the ‘nodeinfo’ service are mapped to
the ‘&mapping’ anchor. This requires placing the anchor before all the other key-
value pairs in the service.
The mappings are then merged into the ‘echoit’ service by ensuring that the
first pair of key and value are ‘<<’ and the alias (*mapping) respectively. Since the
values for ‘environment’ are different than the same option in the ‘nodeinfo’
service, the ‘environment’ configuration is overridden in the ‘echoit’ service by
specifying the new keys and associated values.
2.5 Microservices Architecture
Section 2.4.4 provided an overview of the Docker Compose tool. When this tool is
used to start the containers of an application, it sets up a single network for all the
containers. All these containers linked by the Compose tool are connected to this
network [22]. When the containers are all connected over a single network, they
communicate via an architectural style known as a microservices architecture,
where each container acts as a service for the whole application [23].
In a microservice architecture, each service is an autonomous process and
communication between the services require a mechanism known as inter-process
communication (IPC). There are many forms of IPC mechanisms that can be used
by services. These different forms of service interactions can be classified along to
several dimensions. The first dimension defines the interaction as either
Background | 19
single-sender-single-receiver or as single-sender-multiple-receiver. In the
single-sender-single-receiver interaction style, only one service is responsible for
handling a client’s request; whereas, in the single-sender-multiple-receiver
interaction style, multiple services take responsibility for handling a client’s
request [24].
The second dimension of interaction styles defines whether the communication
between the services is synchronous or asynchronous. In a synchronous
communication style, the client expects a service to send a response within a
certain time window and will possibly block until it receives the response. The
requests and responses sent to and from the client in this communication style
must follow a synchronous protocol, such as Hypertext Transfer Protocol (HTTP).
In addition, any service that handles the client’s request needs to send an
immediate response, within at most a few seconds. However, this is not the case in
asynchronous communication where the client service does not wait for a response
from another service. Moreover, if it is waiting for a response, it does not block
until the response is received. The requests sent to and from the client in this
asynchronous communication style must follow an asynchronous protocol, such as
Advanced Message Queueing Protocol (AMQP) [24].
When services follow the single-sender-single-receiver style synchronously with
requests and responses, the client simply sends a request to another service and
expects a response within a certain time window, whilst blocking until the response
is received. However, when services use the same style asynchronously, they do so
either with notifications or requests and asynchronous responses. When using
notifications, a client simply sends a request without expecting a response.
Sometimes, responses are never sent back to the client. When communicating with
requests and asynchronous responses, the client sends a request to a service and
the service sends a response asynchronously. In this case, the client does not block
until it receives a response [24].
When a service follows the single-sender-multiple-receiver style and
communicate asynchronously, they do so either by publishing/subscribing or with
publishing and asynchronous responses. When using the former, the client
publishes a message as a notification and this message is consumed by at least one
service or no service at all. In the latter, the client publishes a message as a request
and expects a response within a certain time window. Synchronous communication
is usually not used for services following the single-sender-multiple-receiver style,
since this may cause the entire architecture to fail if one of the services fail [24].
Typical applications use a combination of IPC mechanisms [24].
2.5.1 Asynchronous Message-based Communication
When using a message-based IPC mechanism, services in a microservice
architecture interact by exchanging messages via asynchronous protocols, such as
AMQP or Streaming Text Oriented Message Protocol (STOMP). In this case, a
client sends a message to a service, either as a notification or a request. If the
20 | Background
message sent is a request, this typically means that the service needs to send a
response back to the client, which is sent as a message. In this communication
style, the client does not expect to receive the response immediately. Moreover, in
the case that the original message sent was a notification, the receiving service may
not even send a response. This communication style supports a wide variety of
messaging systems, such as RabbitMQ and Apache Kafka. Furthermore, in
message-based communication, whether single-sender-single-receiver style or
single-sender-multiple-receiver style, the message contains a header (which
contains metadata) and a message body [24, 25].
In the single-sender-single-receiver interaction style, the services communicate
over a point-to-point communication channel, where a client sends a message to
exactly one service via the channel. Typically, the message is only processed once;
however, in some cases (such as system failures), the message would need to be
sent multiple times by the client. Typically, the communication in this interaction
style is one-way, as the client does not expect a reply from the service and the
message sent merely acts as a notification. Figure 2-3 shows how a product-
ordering application might make use of a point-to-point communication channel
[25].
Figure 2-3: Asynchronous message-based communication in a point-to-point channel,
data taken from [25]
In the single-sender-multiple-receiver interaction style, services communicate
over a publish-subscribe communication channel, where a service delivers a
message to several other services via the channel [24]. In this interaction style,
when there is a change within the domain of a service that other services need to be
notified of, the service publishes an integration event. Other services subscribe to
these events so they can asynchronously receive them. When this occurs, the
receiving services may update their own domain entities, which can result in more
integration events being published. This publish-subscribe channel typically
requires an event bus, which acts as an interface that has the API needed to
perform actions, such as publishing an event and subscribing (and unsubscribing)
to an event [25]. Figure 2-4 shows how a product-ordering application might use a
publish-subscribe channel.
Background | 21
Figure 2-4: Asynchronous message-based communication in a publish-subscribe
channel, data taken from [25]
2.5.2 Request-Response Communication
The request/response-based IPC mechanism can be used for both synchronous and
asynchronous communication. When using this mechanism, the client sends a
request to a service and the service sends a response back to the client after
processing the request, all of which is done by the service within a certain time
frame. This time frame is usually less than a second or has an upper limit of a few
seconds [26].
Using the request/response-based mechanism for synchronous communication
requires a synchronous protocol, such as HTTP. One of the most commonly used
architectural styles for this IPC mechanism is Representational State Transfer
(REST). REST is based on HTTP and provides the four standard CREATE, READ,
UPDATE, DELETE (CRUD) operations [26]. Figure 2-5 shows how an HTTP based
request/response communication may take place in an application where creating a
resource in a service requires the service to get the user’s ID from a different
service.
Figure 2-5: Synchronous request/response-based communication via REST, taken from
[23]
22 | Background
Other forms of frameworks that can be used instead of REST include Apache
Thrift. A Thrift interface consists of at least one service, where each service is a
collection of multiple methods, imitating a Java interface. Thrift methods can
either return a value, where the communication must be request/response-based
and the client expects to receive responses within a certain time frame or they can
be defined as one-way, where the communication is message-based, and then each
message only acts as a notification [24].
2.6 Reverse proxies
In the general sense, a proxy server is a gateway server that sits between the clients
and the servers and forwards requests from the different clients to servers
(frequently across the Internet). A reverse proxy server is a special type of proxy
server that usually sits behind a firewall in a private network and forwards requests
from clients to the appropriate server among multiple web servers in the
backend [27]. It is primarily used by server administrators to control the amount of
traffic between clients and the servers [28]. Figure 2-6 shows the general concept
and role of a reverse proxy server in a private network.
Figure 2-6: Nginx proxy server
Reverse proxies, such as Nginx, can be used when creating multi-container
Docker applications. The Nginx reverse proxy acts as a gateway between the client
and the containers in the Docker network, creating a client-server architecture
emulating that shown in Figure 2-6. When using the Nginx reverse proxy in a
Docker network, one must specify a virtual host for every service that must be
reachable from the reverse proxy in the network. This typically requires the
VIRTUAL_HOST environment variable in the docker-compose.yml file. Nginx
listens on port 80 and looks for any container with this environment variable
specified and routes traffic to the correct container [29]. The reverse proxy can also
be made to support forwarding to containers with other environment variables,
which would require much further configuration of the reverse proxy.
Background | 23
2.7 REST
REST is an architectural style that defines a standard for creating web services.
A RESTful API is based on this architectural style. A RESTful API, also known
as a RESTful web service, is an API that takes advantage of the HTTP requests:
GET, PUT, POST, and DELETE. This allows requesting systems to access and
manipulate XML-representations of web resources via stateless operations [30].
2.7.1 Architectural Constraints of REST API
Six architectural constraints should be followed when creating a REST API [31]:
1. Requests for the same resource from different clients must require the same
Uniform Resource Identifier (URI).
2. There should be a separation between client and server. All communication
between client and server must occur through requests and responses.
3. All calls must be stateless. All the information that the server needs to know
must be contained within the request.
4. Information regarding whether data delivered by a server is cacheable or not
must be contained within the server’s response. A version number must be
included in any cacheable resources delivered by the server. This will prevent
the client from requesting the same resource multiple times.
5. Requests and responses should not be affected even if several layers of servers
exist between the client and the responding server.
6. A server’s response may optionally contain code that can be executed by a
client.
2.7.2 Request Anatomy
A request sent by a client has four components: Endpoint, method, header(s), and
data [32].
2.7.2.1 Endpoint
The endpoint itself is made up of three components: the root-endpoint, the path,
and the query parameters [32]. Listing 2-10 shows an example of a request
endpoint that contains all three of these components.
Listing 2-10: Example of a request endpoint (Github API)
https://api.github.com/users/Billy/repos?sort=pushed
Listing 2-11: Example of a request endpoint (Canvas API)
https://canvas.instructure.com/api/v1/courses
24 | Background
In the above listing, ‘https://api.github.com’ represents the root-endpoint.
This is the initial part of the API that a response is being requested from (in this
case, the Github API). The portion ‘/users/Billy/repos’ represents the path.
This is the resource that is being requested (in this case, a list of repositories by the
user Billy). Finally, the portion ‘sort=pushed’ represents a query parameter. Query
parameters allow the request to be modified by specifying key-value pairs and
starts with a question-mark symbol (‘?’). If additional query parameters are added,
then each key-value pair must be separated with the ‘and’ symbol (‘&’). In this
particular example, the query parameter ensures that only the repositories that
were pushed recently are returned [32].
Listing 2-11 shows another example of a request endpoint with the Canvas API.
In this example, ‘https://canvas.instructure.com’ represents the root
endpoint and ‘api/v1/courses’ represents the path. In this example, the resource
being requested is the list of active courses for the current user [33].
2.7.2.2 Method
The method component specifies the type of request sent to the server. The five
types of methods are GET, POST, PUT, PATCH, and DELETE. These methods are
used to perform the four CRUD operations [32]. These methods are described in
Table 2-1.
Table 2-1: HTTP methods in a request
Method Description
GET The GET method is used when a resource is to be retrieved from
the server. The server will notify the client if the requested
resource was successfully delivered or not.
POST This method is used when a resource is to be created on the
server. The server will notify the client if the resource was
successfully created or not.
PUT/PATCH The PUT or PATCH method is used when a resource is to be
updated on the server. The server will notify the client if the
resource was successfully updated or not.
DELETE The DELETE method is used when a resource is to be deleted
from the server. The server will notify the client if the resource
was successfully deleted or not.
2.7.2.3 Headers
Headers provide information to both the client and the server. They are
represented as key-value pairs, where the key and the value are separated by a
colon (‘:’). For instance, the header ‘Content-Type’: ‘application/json’ tells
the server to expect content written in JSON [32].
Background | 25
Headers can also be used for authentication purposes. For instance, the header
'Authorization': 'Bearer ' "#{access_token}" is used to tell the server
that the bearer of the access token is authorized to access the API [32].
2.7.2.4 Data
The data is transferred in the “body”. The data is information that is to be sent to
the server and can only be used with POST, PUT, PATCH or DELETE requests [32].
2.8 Related work
This section describes some of the previous projects which use similar tools and
concepts introduced in this chapter.
2.8.1 Connecting Silos
At KTH Royal Institute of Technology, students can submit their thesis drafts to
their examiner via Canvas. Once the thesis is approved by the examiner, the next
step is to archive and optionally publish the thesis via DiVA*, an institutional
repository that stores metadata and documents (such as student theses and
research publications).
Given the large number of theses annually submitted by students, the overall
process of manually entering the metadata and archiving students’ theses in DiVA
is both time-consuming and prone to error. These problematic aspects are largely
due to the need to extract and enter all the metadata necessary for each thesis that
is to be archived. Furthermore, administrative personnel must ensure that this
metadata is correct.
Two students (Shiva Besharat Pour and Qi Li) at KTH conducted a project to
automate the archiving and publishing process [2]. Their solution was to parse PDF
documents and use information from the Canvas LMS to automatically generate a
cover for a given thesis and to extract the required metadata for DiVA. Because
their solution automatically extracted the required data from the thesis itself and
from the LMS, it ensured the correctness of the metadata. As a result, the solution
greatly reduced the amount of time required for archiving and publishing a thesis
in DiVA, while also greatly increasing the accuracy of the metadata for each thesis.
This accuracy was computed by comparing their solution with the accuracy of 100
randomly selected manually entered theses.
2.8.2 On-demand laboratory working environments for Internetworking e-learning
The concept of using Docker containers and LTI specifications to create Canvas
plugins was explored by Andreas Kokkalis in a Master’s thesis project at KTH [10].
His project involved the generation of on-demand virtual exercise environments
using cloud infrastructures and integration of such environments with an LMS to
provide an efficient e-learning experience in an Internetworking course.
* http://kth.diva-portal.org/
26 | Background
Many of the technical concepts in that project are useful for the creation of a
dynamic survey which is to be configured via the LTI specification and invoked
from a course in the KTH instanced of the Canvas LMS.
2.8.3 Using custom columns for metadata data about a thesis
In an approach proposed by Prof. Maguire of KTH for managing degree projects via
a Canvas course, the data collected from the paper form or from a survey is used to
populate custom columns in the Canvas gradebook for a course. Some information
about adding, deleting, and other operations for a custom column can be found in
https://kth.instructure.com/courses/11/pages/adding-a-custom-column-to-
gradebook?module_item_id=9112.
2.8.4 An LTI application for automatic grading
In a paper written by Gajo Petrović, Aleksandar Nikolić, Milan Segedinac,
Aleksandar Kovačević, and Zora Konjović entitle ‘Grader: An LTI app for
automatic, secure, program validation using the Docker sandbox’, the concepts of a
Docker sandbox, REST API interface, and LTI are explored for implementing an
automatic and secure program validation tool in Canvas [34].
2.9 Summary
Canvas is an LMS that contains several tools that allow instructors to control and
administer their courses. Instructors can use these tools to provide learners with
resources and assignments that are relevant to the course. These learners can use
these resources and assignments to manage their studies.
Docker is a program that allows virtualization without the overhead of a
separate kernel for each virtualized system. This allows Docker containers to be
rapidly instantiated when needed. Docker containers also provide the benefits of
portability and sandboxing.
LTI is a standard created by IMS Global Learning Consortium that allows LMSs
to communicate with external tools in a standardized way across all LMSs.
The RESTful API is an architectural style that allows requesting systems to
retrieve and manipulate XML-representations of web resources.
Methodology | 27
3 Methodology
This chapter delineates the methods followed in order to reach the goals of the
project.
3.1 Research Process
The initial phases of the project involved an extensive research stage which aided in
the implementation of the final system. This included acquiring a strong
understanding of the scope of the project, primarily in terms of the project
prerequisites, as well as the resources available, which in itself added more sub-
stages to the overall research process.
3.1.1 Identifying research areas
The first stage of the research process was to identify the different areas that would
need to be researched for the final system that was to be implemented and tested.
Given the scope of the project, this initially proved to be a challenge. Hence, in
order to simplify this process, a knowledge ordering strategy known as the “Top-
down approach” was used. In this strategy, the whole problem is broken down into
multiple sub-problems, which can be further broken down into more
sub-problems. This overall process of dividing the whole problem into multiple
several parts allowed a better understanding of the different submodules of the
final system, which in turn aided in formulating the solutions and identifying which
areas needed to be researched [35]. Figure 3-1 shows an illustration of how the
main problem was broken down into smaller problems with the Top-down
approach.
Figure 3-1: Top-down approach to break the main problem into multiple sub-problems
Dynamic quiz Dynamic quiz
Simulation of Canvas Simulation of Canvas
Microservices Microservices
Docker containers Docker containers
Reverse proxy Reverse proxy
Docker Compose Docker Compose
The quiz program The quiz program
Accessing student data Accessing student data
REST API REST API
Accessing gradebook cells
Accessing gradebook cells
REST API REST API
Embedding into Canvas as an external tool
Embedding into Canvas as an external tool
LTI LTI
28 | Methodology
3.1.2 Literature study
Once all the sub-modules of the final system were identified, the literature study
phase was initiated. This primarily involved researching the different sub-modules
of the final system. Chapters 1 and 2 of this thesis contain all the information that
was collected during the literature study phase.
3.1.3 Identifying Areas of Implementation for the Dynamic Quiz Program
As seen in Figure 3-1, one of the identified submodules was the retrieval of student
data. However, the figure only shows the area that needed to be researched in order
to implement this submodule. During the implementation phase, this submodule
was further broken down into several parts which were initially identified through
the top-down approach described in Section 3.1.1. There were many functions
involving the access of student data which needed to be implemented. Table 3-1
shows and described these different functions that involved access to student data
from the dynamic survey program.
Table 3-1: Submodules for retrieving student data
Module requiring implementation Description of the submodule
Initial setup A set of fake students are created to
experiment with.
Primary Initializations for REST
API calls
Certain variables are initialized and
certain files are imported to ensure that
RESTful API calls can be made within
the program.
Retrieval of Students’ Program
Data
The RESTful API is used to retrieve the
user’s study program (i.e., the study
plan of a student taking the quiz).
Retrieving Students’ General Data Students are presented with general
questions regarding their degree project
and their answers are saved for future
reference.
Displaying Course Options Students are given the option to select
their degree project course based on
their previously-selected choice of
grading scale.
Assigning an Examiner Students are given the option to select
an examiner based on their previously-
selected choice of degree project course.
Methodology | 29
3.1.4 Identifying Requirements
Identifying the areas of implementation for the dynamic survey eased the process
of identifying requirements that need to be met by the dynamic survey in order for
it to be successful. Table 3-2 shows the requirements that were mapped out. The
team decided on using integration tests to determine whether the dynamic survey
has met the requirements. If all requirements are fulfilled by the dynamic survey, it
would be considered that the integration tests have been successful.
Table 3-2: Requirements for integration tests to be successful
Requirement Indicator of the requirement been met
Identify users with no program
data available
If the user has program data available,
the system will directly prompt the user
to enter general data at the start of the
survey. Otherwise, it will prompt the
user to select the program of study.
Identify users from the TIVNM
program
If the user is in the TIVNM program, the
system will notify the user that the A-F
grading scale is mandatory. Otherwise,
it will prompt the user to select a
grading scale.
Prompt the user to select from
only the degree project courses
that are available for the specified
cycle number, the study program,
and the selected (or forced)
grading scale.
The degree project courses selectable by
the user must be in either the
‘AF-course_codes_by_program’ object
or the ‘PF_course_codes_by_program’
object (depending on the grading scale)
as specified in the ‘course-data-EECS-cycle-2c.json’ file.
Prompt the user to select from
only the examiners that are
available for the selected degree
project course.
The degree project courses selectable by
the user must be in the
‘all_course_examiners’ object
specified in the ‘course-data-EECS-cycle-2c.json’ file.
Display the correct contents on the
final page by using the degree
project course and examiner data
retrieved from the user.
Given the degree project course and the
examiner selected by the user, the final
page should say “Thank you for
selecting course code <course_code>
and potential examiner
<selected_examiner>.”, where
<course_code> is the course code of the
selected degree project course and
<examiner> is the selected examiner. If
there were no examiners available for
30 | Methodology
the selected course code, the final page
should say “Thank you for selecting
course code <course_code>. Please
speak with the education office to find
an examiner.”. In this <course_code>
is the course code of the selected degree
project course.
The data entered by the user must
be organized in the gradebook.
The gradebook must have the data
entered by the user under the
appropriate columns.
3.2 Test Bed
This section provides a description of the hardware and software specifications of
all the test and experimentation environments.
3.2.1 Virtual Machine
The Canvas test environment itself was built inside of a virtual machine (VM) that
was running Ubuntu 18.04 (64-bit). During the VM’s setup phase, 8 GM of RAM
from the host system is allocated to the VM. Furthermore, the VM was initialized to
use two processors from the host machine’s CPU. In terms of network settings, the
VM was initially configured to use a bridged network interface. However, due to
some network-related issues in some host systems, the VM was later configured to
use a NAT interface. In terms of storage configurations, the virtual size of the VM
disk storage was 200 GB and the actual size of the VM was 60.1 GB, as the VM used
a dynamically allocated storage system. In terms of USB configurations, the VM
was using a USB 1.1 (OHCI) controller.
3.2.2 Host systems
The host system (a desktop computer), Host-A, used for the VM was running
Windows 10 Pro and had 8 GB of pre-installed RAM. The system type is a 64-bit
operating system with an x64-based processor. The processor itself is an Intel i5.
The internal logic of the dynamic survey system (which was written with Ruby’s
Sinatra framework) was tested on a different host system (a laptop), Host-B, that is
running Windows 10 Home and has 8 GB of pre-installed RAM. The system type is
a 64-bit operating system with an x64-based processor. The processor itself is an
Intel Core i7. Testing the dynamic survey required a source code editor that is
compatible with Ruby code. In this case, Notepad++ was used due to its lightweight
functionality and simplicity. Execution of every ruby file was done from the host
system’s default command prompt terminal.
It is worth mentioning that the VM was originally intended to be used in Host-
B. However, it was later observed that inside the VM, Host-B was unable to load
‘localhost6’ (the page from which the Canvas environment should be accessed
after initiating all the containers). Due to this, the VM was later migrated to Host-
A, where this behavior did not occur.
Methodology | 31
A second observation is that in Host-B, the performance of the VM was
extremely poor when the VM was accessed with Oracle’s VirtualBox (for version
5.2.22 r126460 (Qt5.6.2)). Hence, the VM was later used with a free version of
VMWare Workstation, which showed an improved performance. Nevertheless,
Host-B was still unable to access the Canvas environment through the local host.
3.3 Development and Testing Process
The dynamic survey was written by Prof. Gerald Q. Maguire Jr. and provided to
us as a source for documentation in this thesis. A lot of the functionalities
implemented in the dynamic survey program were in line with the submodules
outlined in Table 3-1. The program also included logic for organizing the student
data into the Canvas gradebook. Section 4.4 of this thesis outlines the
implementation of the dynamic survey program.
Once we were provided with the dynamic survey program, the next step was to
test the program for verification and reliability.
3.3.1 A Variant of Test-driven Development
The primary practices adopted for testing the dynamic survey system were test-
driven development (TDD), and refactoring (most of which was implemented
during the TDD stage) [4]. The testing process did not, however, involve TDD in its
perfect form. Unlike the typical TDD process, where the program would be
developed through tests, this TDD process was implemented after analyzing and
understanding portions of the provided code. This helped us understand what kind
of tests need to be written in order to verify the code written by Prof. Maguire.
In the typical TDD process, a series of test cases are defined for a submodule,
then the submodule itself is repeatedly tested and modified (if needed) until it
passes the test. This software development process acts as a means of running
glass-box tests, where the internal structures of a submodule are known to the
tester. Once the submodule has passed the test, the developer can proceed to the
next submodule [36].
The test-driven nature of the system’s testing phase aided in verifying the
different submodules of the dynamic quiz system. The variant of the TDD process
followed during the implementation of the system consisted of a sequence of four
steps:
Adding test case In this step, test cases were defined for a submodule in a
separate test file before the submodule itself is even
imported into the file. This step helped the project team
focus on the requirement before writing the code for the
submodule. It required a deep understanding of how the
submodule would behave based on the provided code.
Running the tests and
ensuring that the tests
fail
In this step, the testing program was run, and its behavior
was analyzed. As expected, the test led to compilation
errors since the code to be tested had not been added yet.
32 | Methodology
This step acted as a sanity check to ensure that the test
was not flawed.
Writing the submodule In this step, the code for the submodule was added to the
test file. However, the team had to ensure that the added
code was such that it passes the test created in step 1. In
other words, the only requirement of the code was that it
passes the test. No additional functionalities were allowed
in the code.
Running tests again In this step, the code added in step 3 was tested. If the
test cases failed, the team reanalyzed in both the source
code and the test code to find what’s missing, refactored
and ran the test again. This was repeated until the test
was successful, which proved that the code fulfilled the
test requirements.
Once the above steps were completed for a submodule, they were repeated for
the next submodule. Figure 3-2 shows the steps of TDD.
Figure 3-2: TDD sequence
3.3.2 Test case example
A simple example of a test case used during the altered TDD phase is shown in
Listing 3-1. It is worth noting that the code shown in this section are modified
versions of the code in Listing 4-44 in Section 4.4.7. The explanations behind how
the code works are greatly abbreviated in this section and subsequently more
thoroughly explained in Section 4.4.7.
The actual test cases are written between the comments ‘#TEST START’ and
‘#TEST END’. The main goal of the tests was to check whether the route
‘/testCINTEcourses’ manages to successfully retrieve the courses that use the A-F
grading scale and can be taken by 2nd cycle CINTE students. All information
regarding which courses correspond to which program are retrieved from the JSON
file ‘course-data-EECS-cycle-2c.json’. Since the courses II225X and IL228X
are the only courses with A-F grading that can be taken by 2nd cycle CINTE
students, the tests check if contents of the ‘relevant_course_codes’ are II225X in
Methodology | 33
the 0th index and IL228X in the 1st index. If the tests are successful, then the word
“pass” should be printed on the console for each successful test.
Listing 3-1: Test-case for filtering courses
require 'sinatra' require 'json' all_data=JSON.parse(File.read('course-data-EECS-cycle-2c.json')) $AF_course_codes_by_program=all_data['AF_course_codes_by_program'] get '/testCINTEcourses' do #TEST START if relevant_course_codes[0] == "II225X" puts("pass") end if relevant_course_codes[1] == "IL228X" puts("pass") end #TEST END end
As one can already tell, the tests will not pass. This is because no code has been
added to actually retrieve the relevant courses. Hence, the next step is to import the
code from the source program to ensure that the test passes.
The refactored version of the code is shown in Listing 3-2. When the code is
run, it prints “pass” twice on the console (once for each test). This indicates that
the route has successfully extracted the relevant courses and the test has passed.
Listing 3-2: Refactored version of the ‘testCINTEcourses’ route
require 'sinatra' require 'json' all_data=JSON.parse(File.read('course-data-EECS-cycle-2c.json')) $AF_course_codes_by_program=all_data['AF_course_codes_by_program'] get '/testCINTEcourses' do cycle_code='cycle'+"2" relevant_course_codes=$AF_course_codes_by_program[cycle_code]["CINTE"] #TEST START if relevant_course_codes[0] == "II225X" puts("pass") end if relevant_course_codes[1] == "IL228X" puts("pass") end #TEST END end
34 | Methodology
3.4 Evaluation of the process
The overall process was evaluated by running two forms of tests. The first test can
be classified as ‘white-box testing’ and was run from the developer’s point of view.
This test primarily involved re-writing the core components of the entire system
through the variant of the test-driven process described in Section 3.3.1. This
process aided in the verification of every submodule by ensuring that all test cases
were fulfilled by each submodule, as well as increasing the overall reliability of the
system.
The second set of tests performed were integration tests which can be classified
as ‘black-box testing’. This second set of tests was run from the users’ point of view.
This involved testing the final system to ensure that all the submodules interacted
properly and that the system meets the goals of the entire project. This testing
procedure acted as a means of validating the entire system [37].
Implementation | 35
4 Implementation
This chapter outlines the set-up and configuration of the test environment, as well
as the implementation of the final system. Section 4.1 delineates the environment
set-up process. Section 4.2 outlines some additional modifications that were made
during the configurations. Section 4.3 outlines the interprocess-communication
(IPC) occurring between the services in the test environment. Section 4.4
thoroughly delineates the steps taken to implement the dynamic survey and section
4.5 describes the steps taken to embed the dynamic survey into the Canvas LMS as
an external tool.
4.1 Initial setup
The actual development environment for the Canvas LMS was created by following
the Quick-start instructions in the public Github repository*. The version of the
development environment created lacks several features from the actual LMS, such
as an email system and a proper application server. Furthermore, this version is
not intended for commercial usage. Nevertheless, the features it did contain were
sufficient for our experimentation.
Once every dependency was satisfied (by installing the required software), the
creation of the development environment was complete. The resulting virtual
machine (VM) was exported and uploaded online for potential re-use. This allows
any party interested in working with the Canvas LMS development environment to
start experimenting right away, without the inconvenience of rebuilding the
environment. Due to the size of the exported VM as an OVA file and the limit on the
size of a single file in KTH’s Box storage, the OVA file was split into two parts:
ubuntu_for_Canvas-20190317aa https://kth.app.box.com/s/z6b9wlu2ngs886ucl8a90auu3uahw39n and
ubuntu_for_Canvas-20190317ab https://kth.app.box.com/s/unplqnuvsqyevglj2sw2137d6jnqe6qi. The
two parts were concatenated with the command: ‘cat ubuntu_for_Canvas-20190317aa ubuntu_for_Canvas-20190317ab > ubuntu_for_Canvas-20190317a.ova’.
By default, the VM is configured for 8 GB of physical memory. It was also built
with a NAT interface. However, this setting can be changed by developers through
the VM’s settings prior to booting the VM.
After the VM has booted, the first step was to log in and create one or more
terminal windows. Listing 4-1 shows the required authentications for logging in to
the system in the VM.
Listing 4-1: Authentications required for logging in to the Ubuntu system in the VM
user: Chip Maguire
password: chip
* https://github.com/instructure/canvas-lms/wiki/Quick-start
36 | Implementation
The next step is to initiate Dory in one of these windows. Dory is a program
which initiates Dnsmasq (a Domain Name System (DNS) forwarder which
performs domain name-to-address resolutions) and an Nginx reverse proxy server
(which in turn allows for smoother communication between the many services
within the LMS development environment).
Before dory is started up, its status can be checked with the ‘dory status’
command, as shown in Listing 4-2.
Listing 4-2: The output in the bash terminal when the 'dory status' command is executed
maguire@chipcanvas:~$ dory status
[*] Nginx proxy is not running
[*] Dnsmasq is not running
[*] Resolv: configured with nameserver 127.0.0.1
As expected, Listing 4-2 shows that the Nginx reverse proxy and Dnsmasq have
not yet been initiated. Furthermore, it shows that all addresses defined in Dory’s
resolv.conf file will be routed to the nameserver running at the IP address
127.0.0.1.
The actual initiation of Dory is done with the ‘dory up’ command, as shown in
Listing 4-3, together with the corresponding output in the terminal window.
Listing 4-3: Execution of the 'dory up' command in the Ubuntu terminal
maguire@chipcanvas:~$ dory up
Successfully started nginx proxy
docker: Error response from daemon: driver failed programming external
connectivity on endpoint dory_dnsmasq
(b810277461c310d73045a59b7ab1a197eaab9c2093e999d19591616ae7cbe785): Error
starting userland proxy: listen tcp 0.0.0.0:53: bind: address already in use.
You have some systemd services running that will race against us
to bind to port 53 (and usually they win):
NetworkManager.service, systemd-resolved.service
If we don't stop these services temporarily while putting up the
dnsmasq container, starting it will likely fail.
Would you like me to put them down while we start dns
(I'll put them back up when finished)? (Y/N): Y
Requesting sudo to stop NetworkManager.service
[sudo] password for maguire:
Requesting sudo to stop systemd-resolved.service
Implementation | 37
Putting down services succeeded
Requesting sudo to check if something is bound to port 53
Requesting sudo to start systemd-resolved.service
Waiting 5 seconds for systemd-resolved.service to start ...
Requesting sudo to start NetworkManager.service
Waiting 5 seconds for NetworkManager.service to start ...
NetworkManager.service, systemd-resolved.service were successfully restarted
Requesting sudo to start systemd-resolved.service
Waiting 5 seconds for systemd-resolved.service to start ...
Requesting sudo to start NetworkManager.service
Waiting 5 seconds for NetworkManager.service to start ...
NetworkManager.service, systemd-resolved.service were successfully restarted
Successfully started dnsmasq
Successfully configured local resolver
As shown in Listing 4-3, execution of the dory up command initiates the Nginx
reverse proxy server and Dnsmasq and configures the local resolver (with some
delay in between).
At this point, if the one wishes to know if Dory is doing its job properly (i.e.
routing the DNS requests for the canvas.docker domain to the correct IP address),
the ‘dig canvas.docker’ command can be used as shown in Listing 4-4. Note that
in the listing, the output command returns a ‘NOERROR’ status.
Listing 4-4: Execution of the 'dig canvas.docker' command in the Ubuntu terminal
maguire@chipcanvas:~$ dig canvas.docker
; <<>> DiG 9.11.3-1ubuntu1.2-Ubuntu <<>> canvas.docker
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 21519
;; flags: qr aa rd ra ad; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0
;; QUESTION SECTION:
;canvas.docker. IN A
;; ANSWER SECTION:
canvas.docker. 0 IN A 127.0.0.1
38 | Implementation
;; Query time: 0 msec
;; SERVER: 127.0.0.1#53(127.0.0.1)
;; WHEN: Wed Dec 12 15:04:06 UTC 2018
;; MSG SIZE rcvd: 47
To actually start up the Canvas development environment, the ‘docker-
compose up’ command is executed from the canvas directory in the VM. This will
initiate each of the Docker containers (which act as separate services that realize
separate parts of the Canvas environment). This command and the corresponding
output (shown in Figure 4-1) indicates the configuration and initiation of these
containers in the back-end succeded.
Figure 4-1: Initial output of the 'docker-compose up' command
The virtualized Canvas LMS environment will be ready when the final set of
output is displayed in the terminal window (see Figure 4-2).
Implementation | 39
Figure 4-2: Final output of the 'docker-compose up' command
At this point, a list of all the running containers can be displayed by executing
the ‘docker-compose ps’ command from a different terminal window (see Listing
4-5).
Listing 4-5: Listing all the running containers with the ‘docker-compose ps’ command
maguire@chipcanvas:~/canvas$ docker-compose ps
Name Command State Ports
---------------------------------------------------------------------
canvas_jobs_1 bundle exec script/delayed ... Up 80/tcp
canvas_postgres_1 docker-entrypoint.sh postgres Up 5432/tcp
canvas_redis_1 docker-entrypoint.sh redis ... Up 6379/tcp
canvas_web_1 /tini -- /usr/src/entrypoint Up 80/tcp
canvas_webpack_1 yarn run webpack Up 80/tcp
After all containers are loaded, it usually takes some time for the Canvas page to
load in the browser. The page can be accessed by visiting http://canvas.docker. If
there were no issues when loading the containers, then the user should be directed
to the Canvas login page shown in Figure 4-3.
40 | Implementation
Figure 4-3: The Canvas log-in page
The authorizations required to log in to Canvas as the administrator is
‘[email protected]’ as the email address and ‘chipcanvas’ as the
password. After logging into Canvas, the home page of the test course is loaded, as
shown in Figure 4-4.
Figure 4-4: Home page of the test course
4.2 Some additional modifications to the system
Although the steps shown in Section 4.1 should be sufficient to set up all the
containers and load the virtualized Canvas environment, they are still flawed.
Occasionally there would be unexpected error messages that hindered the setup
process. This section shows some of the additional changes made to the setup files.
Implementation | 41
It should be noted that if the one wishes to stop all running containers, one may
do so by running the ‘docker stop $(docker ps -aq)’ command. Once all
containers have been stopped, one can remove all the created containers by
running the ‘docker rm $(docker ps -aq)’ command.
However, if one only wishes to stop one certain container, one must use the
‘docker stop <container-name>’ command, where <container-name> is the
name of the desired container to be stopped, as listed under the NAMES heading
after running the ‘docker ps’ command. One can even use ‘docker stop <container-id>’, where <container-id> is the ID of the desired container to be
stopped, as listed under CONTAINER ID after running the ‘docker ps’ command.
4.2.1 Changing the overcommit handling mode
Due to some error messages displayed in the output of the command ‘docker-compose up’, some additional changes were made to the ‘/etc/sysctl.conf’ file.
The line ‘vm.overcommit_memory = 1’ was added to the file, to set the kernel’s
overcommit handling mode to 1, thus ensuring that OS always overcommits. This
way, the system will keep giving out virtual memory after a call to allocate more
memory regardless of whether it knows if sufficient memory exists to fulfill the
call [38]. In order for the change to take effect, the command ‘sysctl vm.overcommit_memory=1’ had to be executed (see Listing 4-6).
Listing 4-6: Setting overcommit handling mode to 1
maguire@chipcanvas:~$ sudo sysctl vm.overcommit_memory=1
[sudo] password for maguire:
vm.overcommit_memory = 1
4.2.2 Turning off hugepages
Due to some hugepage-related error messages in the output of ‘docker-compose up’, the lines shown in Listing 4-7 were added to the ‘/etc/rc.local’ file.
Listing 4-7: Turning off hugepages
# turn turn off hugepages for redis
if test -f /sys/kernel/mm/transparent_hugepage/enabled; then
echo never > /sys/kernel/mm/transparent_hugepage/enabled
fi
# to enable more TCP file descriptors
sysctl -w net.core.somaxconn=65535
42 | Implementation
Then the ‘/etc/rc.local’ file was made executable with the command shown
in Listing 4-8.
Listing 4-8: Making the '/etc/rc.local' file executable
chmod ug+x /etc/rc.local
Finally, a series of commands (shown in Listing 4-9 along with their
corresponding outputs) were run to ensure that the ‘/etc/rc.local’ file is
executed.
Listing 4-9: Ensuring that the '/etc/rc.local' file is executed
maguire@chipcanvas:~$ cat /sys/kernel/mm/transparent_hugepage/enabled
always [madvise] never
maguire@chipcanvas:~$ sudo /etc/rc.local
[sudo] password for maguire:
net.core.somaxconn = 65535
maguire@chipcanvas:~$ cat /sys/kernel/mm/transparent_hugepage/enabled
always madvise [never]
After running these commands, when Dory and Docker Compose were initiated
(as shown in Section 4.1), the output of ‘docker-compose up’ no longer displayed
any hugepage-related error messages.
4.2.3 Using a specific Bundler version
Some additional changes were made to the file ‘common.sh’, which resides in the
‘~/canvas/script/’ directory. These changes are shown in Listing 4-10. The
reason for these changes is that the configuration files set an upper bound on the
Bundler version that can be used. Hence, the latest version of Bundler is
uninstalled and the older version 1.16.1 is used instead.
Listing 4-10: Using a specific Bundler version
function bundle_install {
echo_console_and_log " Installing gems (bundle install) ..."
rm Gemfile.lock* >/dev/null 2>&1
#added by gqmjr
gem uninstall --all --ignore-dependencies --force $BUNDLER_INSTALL bundler &&
gem install bundler --no-document -v 1.16.1
gem install bullet
bundle install >>"$LOG" 2>&1
}
Implementation | 43
4.2.4 Changing the version of the active_model_serializers gem
Some changes were made in the ‘app.rb’ file which resides in the
‘/canvas/Gemfile.d’ directory. These changes were made to prevent the 0.9.0
alpha1 version of the ‘active_model_serializers’ gem from loading and to
ensure that the 0.9.3 version of the gem loads instead. These changes are shown in
Listing 4-11.
Listing 4-11: Changes to the 'app.rb' file
#commented the following lines out and loaded version 0.9.3 - GQMJr
#gem 'active_model_serializers', '0.9.0alpha1',
# github: 'rails-api/active_model_serializers', ref:
'61882e1e4127facfe92e49057aec71edbe981829'
gem 'active_model_serializers', '0.9.3'
4.2.5 Changing the bullet version
Some changes were made in the ‘assets.rb’ file, which resides in the
‘~/canvas/Gemfile.d/’ directory. The changes were made to ensure that the 5.9.0
version of bullet is loaded, instead of the 5.7.5 version. This change is shown in
Listing 4-12.
Listing 4-12: Changes to the 'assets.rb' file
# gem 'bullet', '5.7.5', require: false, github: 'flyerhzm/bullet', ref: '1677bc0ff78ed550e82a8fd5ecb97f64c7e83e36'
# added bullet - GQMJr
gem 'bullet', '5.9.0'
4.3 A Glimpse into the Interprocess Communication with Wireshark
In order to better understand the interaction between the Docker containers in the
network, a Wireshark capture was performed on all the bridge networks created by
Dory and Docker Compose. The bridge networks were viewed with the command
‘sudo brctl show’, which resulted in the output shown in Listing 4-13.
Listing 4-13: The bridge networks
bridge name bridge id STP enabled interfaces br-3a6597a77b3f 8000.0242e18c3b22 no veth23a5918 veth79ab51b veth91725c8 vethc46e455 vethe427ddd vethf153765 docker0 8000.024251d1c25c no veth24a3080 vethb585b6f
As Listing 4-13 shows, there are two Docker bridges created: docker0 and br-
3a6597a77b3f. The IP addresses of these bridges were found with the command ip route show, which led to the output in Listing 4-14.
44 | Implementation
Listing 4-14: IP addresses of the bridge networks
default via 10.0.2.2 dev enp0s3 proto dhcp src 10.0.2.15 metric 100 10.0.2.0/24 dev enp0s3 proto kernel scope link src 10.0.2.15 10.0.2.2 dev enp0s3 proto dhcp scope link src 10.0.2.15 metric 100 172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1 172.18.0.0/16 dev br-3a6597a77b3f proto kernel scope link src 172.18.0.1
As Listing 4-14 shows from the client’s perspective, the address of the interface
connected to the docker0 bridge (172.17.0.1) and of the interface connected to the
br-3a6597a77b3f bridge (172.18.0.1).
The next step was to identify the IP addresses of all the containers, which was
achieved with the command ‘docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' container_name_or_id’, where container_name_or_id represents the name of
the docker container. This command was executed for each individual container.
The resulting IP address of each container is shown in Table 4-1.
Table 4-1: IP addresses of all the docker containers
Container name IP address dory_dinghy_http_proxy 172.17.0.2 (in the docker0 network) dory_dinghy_http_proxy 172.18.0.5 (in the br-3a6597a77b3f network)
dory_dnsmasq 172.17.0.3 canvas_web_1 172.18.0.6 canvas_webpack_1 172.18.0.4 canvas_jobs_1 172.18.0.7 canvas_redis_1 172.18.0.3 canvas_postgres_1 172.18.0.2
With the IP addresses of all the networks and the containers known, the next
step was to perform a Wireshark capture. In order to capture the IPC in a certain
network, the first step was to open two Wireshark windows and select the correct
interface in each window. In the first window, the docker0 interface was selected to
capture all the traffic via the docker0 interface. In the second window, the
br-3a6597a77b3f interface was selected to capture all the traffic via the
br-3a6597a77b3f interface. Once the capture was initiated in both windows, we
navigated to ‘http://canvas.docker/courses/4/assignments/2’, which is home to
the dynamic survey assignment. Once the dynamic survey was loaded onto the
screen, the capture was stopped in both Wireshark windows so that the packet
transfer could be inspected. The goal was to obtain an understanding of IPC that
takes place when the virtualized Canvas environment is used. The URI
‘http://canvas.docker/courses/4/assignments/2’ was used as a test. The objective
of the test was to inspect the IPC starting from when a request to the URI
‘http://canvas.docker/courses/4/assignments/2’ is made from the browser until
the point when the response for this URI is received. Figure 4-5 shows the GET
request in Wireshark’s ‘Packet Lists’ pane.
Implementation | 45
Figure 4-5: GET request in the Packet List pane (docker0 interface)
In the Packet List pane shown in Figure 4-5, packet no. 9 is selected. The
selected field shows information about the packet. The symbol to the left side of
the packet number indicates that this packet is being sent as a request, whereas the
symbol indicates that the packet is being sent as a response (which is the case for
packet 29 in the figure) [39]. The ‘Protocol’ field of the packet indicates that the
request is using HTTP and the ‘Info’ field indicates that the request is using the
GET method for the URI ‘/courses/4/assignments/2’, which is the path to the
dynamic survey assignment. The ‘Time’ field shows the timestamp of the packet.
As shown in Table 4-1, 172.17.0.2 is the address of the dory_dinghy_http_proxy
container, which contains the Nginx reverse proxy microservice. Packet 10 in the
figure is an ACKnowledgement packet that Nginx sends to the source to indicate
that it has received packet 9. This can be verified if packet 10 is selected, which will
cause a check mark symbol to appear in the ‘No.’ column of packet 9 (indicating
that packet 10 is an ACK to packet 9).
With packet 9 highlighted, the ‘Packet Details’ pane can be inspected to gain
additional information about the packet [40].
Figure 4-6 shows the different protocol fields of packet 9. For example, this
packet is being transferred via Internet Protocol version 4 (IPv4), which in turn is
being used by the Transmission Control Protocol to carry the HTTP request to the
packet destination [41]. The IPv4 field is encapsulated in an Ethernet II frame,
which also shows the source and destination MAC addresses. When expanding the
‘Hypertext Transfer Protocol’ field, the pane also shows that the origin of the GET
request in the ‘User-Agent’ field, which, in this case, is the Firefox browser.
46 | Implementation
Figure 4-6: The details of packet 9 in docker0 interface (docker0 interface)
Figure 4-5 also showed some dashed lines in the ‘No.’ column where the packets
are being sent as Domain Name System (DNS) requests. The dashed lines indicate
that Wireshark does not treat these packets as being part of the conversation [39].
This was further verified with some of the earlier and subsequent Wireshark
captures (one of them shown in Figure 4-7) where these DNS packets were not
present during a GET request to the URI ‘http://canvas.docker
/courses/4/assignments/2’.
Figure 4-7: No DNS packets during GET request to '/courses/4/assignments/2' (different
capture)
The ‘Packet Bytes’ pane can be used to inspect the contents of the packets in
both hex dump and American Standard Code for Information Exchange (ASCII)
format [42]. Figure 4-8 shows the Packet Bytes pane with Packet 9 selected. The
highlighted portion of the hex dump represents the request URI
(/courses/4/assignments/2).
Implementation | 47
Figure 4-8: Contents of Packet 9 (docker0 interface)
The GET request from the browser initiates a TCP handshake protocol via the
br-3a6597a77b3f interface, shown by packets 28-30 in Figure 4-9.
Figure 4-9: TCP handshake and GET request (br-3a6597a77b3f interface)
The horizontal line to the left of packet 28 indicates that the conversation was
initiated from this packet [39]. This packet is a TCP SYNchronize packet from
Nginx to the canvas_web_1 container. When canvas_web_1 receives the packet, it
sends a SYNchronize-ACKnowledgement packet back to Nginx. When Nginx
receives this packet, it sends an ACKnowledgement packet back to canvas_web_1.
When canvas_web_1 receives the ACK packet, the handshake phase is complete
and the TCP connection between Nginx and canvas_web_1 is established. Nginx is
48 | Implementation
now able to forward the GET request to canvas_web_1, which responds with an
ACK packet back to Nginx after receiving the GET request. The timestamps in these
packets show that this part of the conversation takes place after packet 9 and 10 in
Figure 4-5.
Another observation made in this capture is that before any GET request is
made, there is continuous communication between the canvas_jobs_1 container
and the canvas_postgres_1 container. This happens when the user remains idle on
a Canvas page (see Figure 4-10). But once the user clicks the link to the dynamic
survey assignment (which prompts the GET request), communication between the
canvas_web_1 container, canvas_redis_1 container (via TCP) and
canvas_postgres_1 container (mostly via PostgreSQL protocol [43]) becomes more
frequent (see Figure 4-11). The ‘Info’ column of the postgresSQL packets provide
information regarding the query to the Postgres database and the response. For
instance, the ‘>’ symbol shows that the packet is being sent as a query, while the ‘<’
symbol shows that the packet is being sent as a response. ‘1’ and ‘2’ signifies parse
completion and bind completion respectively. ‘Q’ denotes a simple query sent to the
database. The ‘T’ in the response denotes the RowDescription which contains
details on the column structure (Table Object Identifier (IOD), column index, Type
IOD, the format of output (binary or text), etc.). The ‘D’ in the response denotes the
DataRow message (which allows you to see the exact data). Finally, the ‘C’ denotes
the CommandCompletion message and the ‘Z’ denotes the ReadyForQuery message
[44, 45]. The interesting thing here is that the vertical bars for these packets in the
‘No.’ column (next to the packet number) are all in the form of dashed lines, despite
these packets not being present before the initial GET request. This indicates that
even if the communication between web, redis, and postgres occured as a result of
the GET request, Wireshark knows these are different conversations. The dashed
lines indicate that related packets from the conversation can be found further down
the pane.
Figure 4-10: Communication before GET request (br-3a6597a77b3f interface)
Implementation | 49
Figure 4-11: Communication between, web, redis, and postgres (after GET request;
br-3a6597a77b3f interface)
The conversation continues with packet 411, as the canvas_web_1 sends a series
of TCP packets to Nginx and receives an ACK packet for each packet received by
Nginx (packets 411 to 418 in Figure 4-12). Once canvas_web_1 sends the 200 OK
response code and receives the corresponding ACK packet from Nginx (packet 419-
420 in Figure 4-12), it initiates TCP connection termination phase with a FIN &
ACK packet (packet 421 in Figure 4-12)). Before it can receive a FIN & ACK packet
back from Nginx, Nginx sends a series of TCP packets back to the browser and
receives an ACK for each packet the browser receives (packets 15-28 in Figure 4-5).
The data in these TCP packets includes the data from TCP packets 15, 17, 19, 21, 23,
25, and 27 in Figure 4-5. This was verified by analyzing the packet data in the
Packet Bytes pane. Once Nginx is done sending all the TCP packets to the browser,
it sends the FIN & ACK packet to canvas_web_1. Once canvas_web_1 receives the
FIN & ACK packet from Nginx, it sends an ACK back to Nginx and then the
connection is terminated (packets 422-423 in Figure 4-12)). Finally, Nginx sends
the 200 OK response back to the browser, which sends an ACK back to Nginx after
receiving the response (packets 29-30 in Figure 4-5).
50 | Implementation
Figure 4-12: Final phase of the conversation (br-3a6597a77b3f interface)
The Packet Details pane can also be used to inspect the details of a TCP packet sent
from the web service to Nginx (see Figure 4-13). For instance, for packet 412, the
Packet Details pane shows that the TCP protocol is being transferred via IPv4 and
is encapsulated in an Ethernet II frame. The Ethernet II field also shows the source
and destination MAC addresses of the packet. Expanding the [SEQ/ACK analysis]
field (in the TCP field) shows that this packet is an ACK for packet 411. Expanding
the ‘Flags’ field shows what other TCP flags are set in this packet. In this particular
case, only the ACK flag is set.
Figure 4-13: Details of packet 412 (br-3a6597a77b3f interface)
Implementation | 51
Figure 4-14, Figure 4-15, Figure 4-16, and Figure 4-17 illustrate the entire
conversation from the point the HTTP request is sent until the response is received
by the browser. In the figure, a single-lined arrow represents a single packet, and a
triple-lined arrow in both directions represents a continuous transmission of
packets to and from a source (e.g. TCP packets and their corresponding ACK
packets). The numbers in the boxes represent the order of transmission (based on
the timestamps), while the packet number themselves are shown as
P<packet_number> (e.g., P10 means packet 10). Table 4-2 acts as a legend for
interpreting the packet numbers in the figures. In the table, the order of
transmission is indicated by the ‘Step’ column.
Figure 4-14: Initial GET request and handshake phase
Figure 4-15: Communication with the redis and postgres containers
52 | Implementation
Figure 4-16: TCP packet transfer from web to Nginx
Figure 4-17: Connection termination and response
Implementation | 53
Table 4-2: Legend for figures 4-14, 4-15, 4-16, and 4-17
Step Packet
number
Interface Details Protocol
1 9 docker0 GET request from
the browser
HTTP
2 10 docker0 ACK for packet 9 TCP
3 28 br-3a6597a77b3f SYN packet
(handshake start)
TCP
4 29 br-3a6597a77b3f SYN, ACK packet TCP
5 30 br-3a6597a77b3f ACK packet
(handshake done)
TCP
6 31 br-3a6597a77b3f GET request
forwarded to
canvas_web_1
HTTP
7 32 br-3a6597a77b3f ACK for packet 6 TCP
8 42-410 br-3a6597a77b3f Communication of
web with redis and
postgres
TCP (with
redis) and
PostgreSQL
(with
postgres)
9 411-418 br-3a6597a77b3f Stream of TCP
packets sent from
web to Nginx; ACK
received for each
packet
TCP
10 419 br-3a6597a77b3f 200 OK response
sent to Nginx
HTTP
11 420 br-3a6597a77b3f ACK for packet 419 TCP
12 421 br-3a6597a77b3f FIN, ACK sent from
web to Nginx (TCP
connection
termination start)
TCP
54 | Implementation
Step Packet
number
Interface Details Protocol
13 15-28 docker0 Stream of TCP
packets sent from
Nginx to browser;
ACK received for
each packet. (These
TCP packets contain
data sent to Nginx in
Step 9)
TCP
14 422 br-3a6597a77b3f FIN, ACK sent from
Nginx to Web
TCP
15 423 br-3a6597a77b3f ACK sent from web
to Nginx (TCP
termination done)
TCP
16 29 docker0 200 OK response
sent from Nginx to
browser
HTTP
17 30 docker0 ACK for packet 16
sent to Nginx
TCP
As mentioned earlier, the TCP packets sent from Nginx to the browser contain
the data that was initially sent from canvas_web_1 to Nginx. For example, Listing
4-15 shows part of the data in packet 417 (in the br-3a6597a77b3f interface), which
is forwarded by Nginx to the browser in packet 23 (in the docker0 interface). The
data was retrieved by copying the ASCII representation of the packet’s TCP
segment data from the Packet Bytes pane as printable text. Other parts of the data
in packet 417 can also be found in packets 25 and 27 (both in the docker0 network),
which are also sent to the browser by Nginx.
Implementation | 55
Listing 4-15: Partial data from packet 417
<input type="hidden" name="custom_canvas_assignment_points_possible"
id="custom_canvas_assignment_points_possible" value="0" /> <input
type="hidden" name="custom_canvas_assignment_points_possible"
id="custom_canvas_assignment_points_possible" value="0" />
<input type="hidden" name="custom_canvas_assignment_title"
id="custom_canvas_assignment_title" value="Information om
exjobbsprojekt/Information for degree project (v2)" /> <input
type="hidden" name="custom_canvas_assignment_title"
id="custom_canvas_assignment_title" value="Information om
exjobbsprojekt/Information for degree project (v2)" />
<input type="hidden" name="custom_canvas_course_id"
id="custom_canvas_course_id" value="4" /> <input type="hidden"
name="custom_canvas_course_id" id="custom_canvas_course_id" value="4" />
<input type="hidden" name="custom_canvas_enrollment_state"
id="custom_canvas_enrollment_state" value="active" /> <input
type="hidden" name="custom_canvas_enrollment_state"
id="custom_canvas_enrollment_state" value="active" />
<input type="hidden" name="custom_canvas_user_id"
id="custom_canvas_user_id" value="4" /> <input type="hidden"
name="custom_canvas_user_id" id="custom_canvas_user_id" value="4" />
<input type="hidden" name="custom_canvas_user_login_id"
id="custom_canvas_user_login_id" value="s2" /> <input
type="hidden" name="custom_canvas_user_login_id"
id="custom_canvas_user_login_id" value="s2" />
<input type="hidden" name="custom_canvas_workflow_state"
id="custom_canvas_workflow_state" value="available" /> <input
type="hidden" name="custom_canvas_workflow_state"
id="custom_canvas_workflow_state" value="available" />
<input type="hidden" name="custom_sis_id" id="custom_sis_id"
value="z2" /> <input type="hidden" name="custom_sis_id"
id="custom_sis_id" value="z2" />
<input type="hidden" name="custom_user_sis_id"
id="custom_user_sis_id" value="z2" /> <input type="hidden"
name="custom_user_sis_id" id="custom_user_sis_id" value="z2" />
<input type="hidden" name="ext_ims_lis_basic_outcome_url"
id="ext_ims_lis_basic_outcome_url"
value="http://canvas.docker/api/lti/v1/tools/1/ext_grade_passback" />
<input type="hidden" name="ext_ims_lis_basic_outcome_url"
id="ext_ims_lis_basic_outcome_url"
value="http://canvas.docker/api/lti/v1/tools/1/ext_grade_passback" />
<input type="hidden" name="ext_lti_assignment_id"
id="ext_lti_assignment_id" value="183201ba-e1b9-4667-9d7f-ec76b1b27228" />
<input type="hidden" name="ext_lti_assignment_id"
56 | Implementation
id="ext_lti_assignment_id" value="183201ba-e1b9-4667-9d7f-ec76b1b27228" />
<input type="hidden" name="ext_outcome_data_values_accepted"
id="ext_outcome_data_values_accepted" value="url,text" /> <input
type="hidden" name="ext_outcome_data_values_accepted"
id="ext_outcome_data_values_accepted" value="url,text" />
<input type="hidden" name="ext_outcome_result_total_score_accepted"
id="ext_outcome_result_total_score_accepted" value="true" />
<input type="hidden" name="ext_outcome_result_total_score_accepted"
id="ext_outcome_result_total_score_accepted" value="true" />
<input type="hidden"
name="ext_outcome_submission_submitted_at_accepted"
id="ext_outcome_submission_submitted_at_accepted" value="true" />
<input type="hidden" name="ext_outcome_submission_submitted_at_accepted"
id="ext_outcome_submission_submitted_at_accepted" value="true" />
<input type="hidden" name="ext_outcomes_tool_placement_url"
id="ext_outcomes_tool_placement_url"
value="http://canvas.docker/api/lti/v1/turnitin/outcomes_placement/1" />
<input type="hidden" name="ext_outcomes_tool_placement_url"
id="ext_outcomes_tool_placement_url"
value="http://canvas.docker/api/lti/v1/turnitin/outcomes_placement/1" />
<input type="hidden" name="ext_roles" id="ext_roles"
value="urn:lti:instrole:ims/lis/Student,urn:lti:role:ims/lis/Learner,urn:lti:sys
role:ims/lis/User" /> <input type="hidden" name="ext_roles"
id="ext_roles"
value="urn:lti:instrole:ims/lis/Student,urn:lti:role:ims/lis/Learner,urn:lti:sys
role:ims/lis/User" />
<input type="hidden" name="launch_presentation_document_target"
id="launch_presentation_document_target" value="iframe" /> <input
type="hidden" name="launch_presentation_document_target"
id="launch_presentation_document_target" value="iframe" />
<input type="hidden" name="launch_presentation_locale"
id="launch_presentation_locale" value="en" /> <input type="hidden"
name="launch_presentation_locale" id="launch_presentation_locale" value="en" />
<input type="hidden" name="launch_presentation_return_url"
id="launch_presentation_return_url"
value="http://canvas.docker/courses/4/external_content/success/external_tool_red
irect" /> <input type="hidden"
name="launch_presentation_return_url" id="launch_presentation_return_url"
value="http://canvas.docker/courses/4/external_content/success/external_tool_red
irect" />
<input type="hidden" name="lis_outcome_service_url"
id="lis_outcome_service_url"
value="http://canvas.docker/api/lti/v1/tools/1/grade_passback" />
<input type="hidden" name="lis_outcome_service_url" id="lis_outcome_service_url"
Implementation | 57
value="http://canvas.docker/api/lti/v1/tools/1/grade_passback" />
<input type="hidden" name="lis_person_contact_email_primary"
id="lis_person_contact_email_primary" value="bertil@localhost" />
<input type="hidden" name="lis_person_contact_email_primary"
id="lis_person_contact_email_primary" value="bertil@localhost" />
<input type="hidden" name="lis_person_name_family"
id="lis_person_name_family" value="FakeStudent" /> <input
type="hidden" name="lis_person_name_family" id="lis_person_name_family"
value="FakeStudent" />
<input type="hidden" name="lis_person_name_full"
id="lis_person_name_full" value="Bertil FakeStudent" /> <input
type="hidden" name="lis_person_name_full" id="lis_person_name_full"
value="Bertil FakeStudent" />
<input type="hidden" name="lis_person_name_given"
id="lis_person_name_given" value="Bertil" /> <input type="hidden"
name="lis_person_name_given" id="lis_person_name_given" value="Bertil" />
<input type="hidden" name="lis_person_sourcedid"
id="lis_person_sourcedid" value="z2" /> <input type="hidden"
name="lis_person_sourcedid" id="lis_person_sourcedid" value="z2" />
<input type="hidden" name="lis_result_sourcedid"
id="lis_result_sourcedid" value="1-4-2-4-
3ecb800bfaf96fa629d0b8cee8ca68b822bb5906" /> <input type="hidden"
name="lis_result_sourcedid" id="lis_result_sourcedid" value="1-4-2-4-
3ecb800bfaf96fa629d0b8cee8ca68b822bb5906" />
<input type="hidden" name="lti_message_type" id="lti_message_type"
value="basic-lti-launch-request" /> <input type="hidden"
name="lti_message_type" id="lti_message_type" value="basic-lti-launch-request"
/>
<input type="hidden" name="lti_version" id="lti_version" value="LTI-
1p0" /> <input type="hidden" name="lti_version"
id="lti_version" value="LTI-1p0" />
<input type="hidden" name="oauth_callback" id="oauth_callback"
value="about:blank" /> <input type="hidden"
name="oauth_callback" id="oauth_callback" value="about:blank" />
<input type="hidden" name="resource_link_id" id="resource_link_id"
value="6a8aaca162bfc4393804afd4cd53cd94413c48bb" />
58 | Implementation
As a final note, in a subsequent Wireshark capture, it was observed that making
requests to some of the other pages in Canvas led to very similar IPC patterns. One
example is shown in Figure 4-18, Figure 4-19, and Figure 4-20, which show the IPC
when making a GET request for the URI
‘http://canvas.docker/courses/4/modules’. It should be noted that during this
session, the IP address of the Nginx service was 172.18.0.7 (in the br-3a6597a77b3f
interface), the IP address of the canvas_web_1 container was 172.18.0.5, the IP
address of canvas_redis_1 was 172.18.0.3, and the IP address of canvas_postgres_1
was 172.18.0.2.
Figure 4-18: GET request to '/courses/4/modules' (in docker0 interface)
Figure 4-19: Handshake phase and GET request to 'courses/4/modules'
Implementation | 59
Figure 4-20: Response and termination phase
4.4 The Dynamic Survey
This section describes the implementation of a survey implemented by a program
which presents questions in a dynamic fashion. This quiz is designed to collect data
from KTH students seeking to start a degree project. The interaction between the
quiz program (implemented as an LTI tool) and Canvas follows the LTI standard.
This was achieved by first making certain LTI-specific configurations. Additional,
interaction between the program and Canvas is done directly using the Canvas API.
This interaction allows the retrieval of data about a student’s program of study
from Canvas*, which in turn is used by the LTI tool to determine which questions to
skip and which to present.
4.4.1 Modeling the Dynamic Survey
A graphical representation of the dynamic survey was created in the DOT graph
description language using the Graphvix tool [46]. The code is shown in Listing
4-16 and the resulting diagram is shown in Figure 4-21.
* Note that this information is only in this test environment and not in the production Canvas environment. The actual means of getting this information from LADOK is currently being explored by the KTH IT unit.
60 | Implementation
Listing 4-16: Dynamic survey represented in DOT
digraph {
Initiate -> LTI_config
LTI_config -> Try_program_retrieval
Try_program_retrieval -> Prompt_user_for_program[label="Program unknown"]
Prompt_user_for_program -> Program_data_retrieved
Try_program_retrieval -> Program_data_retrieved[label="Program data available"]
Program_data_retrieved -> Prompt_user_for_general_data
Prompt_user_for_general_data -> Force_set_AF_grading[label="User is TIVNM student"]
Prompt_user_for_general_data -> Prompt_user_for_grading_scale[label="User is non-TIVNM student"]
Force_set_AF_grading -> General_data_retrieved
Prompt_user_for_grading_scale -> General_data_retrieved
General_data_retrieved -> Prompt_user_for_relevant_AF_course[label="A-F grading selected"]
General_data_retrieved -> Prompt_user_for_relevant_PF_course[label="P/F grading selected"]
Prompt_user_for_relevant_AF_course -> Course_retrieved
Prompt_user_for_relevant_PF_course -> Course_retrieved
Course_retrieved -> Prompt_user_for_examiner[label="Examiner available"]
Prompt_user_for_examiner -> Examiner_retrieved
Examiner_retrieved -> Store_all_retrieved_information_in_gradebook
Course_retrieved -> Notify_user_to_consult_admissions_office[label="Examiner unavailable"]
}
Implementation | 61
Figure 4-21: Event flow of the dynamic survey
62 | Implementation
4.4.2 Initial Setup Prior to Experimentation
It should be noted that for experimentation purposes, the LTI tool was not tested
with real users and their corresponding program data. Rather, the tool was tested
with fake students that were “custom made” for use in the test environment (i.e.,
the Docker-based Canvas environment). Each student was assigned a Student
Information System (SIS) ID that uniquely identifies one of these students when
making HTTP requests to access data in Canvas. Table 4-3 shows the SIS IDs that
were assigned to each of the fake students.
Table 4-3: SIS ID associated with each fake user
Student name SIS ID
Ann FakeStudent z1
Bertil FakeStudent z2
Cenric FakeStudent z3
David FakeStudent z4
Ellen FakeStudent z5
Fran FakeStudent z6
Gordon FakeStudent z7
Håkan FakeStudent z8
Ibuy FakeStudent z9
James FakeStudent z10
Quentin FakeStudent z13
Furthermore, for testing purposes, the program data for each of the students in
the table was also custom-created. The use of fake students enables testing without
any risk of disclosure of actual students’ personal data. The only actual data in the
system is the name of Gerald Q. Maguire Jr. (as chip.maguire the Linux system
user account and later as a teacher) and the actual names of all of the teachers in
the EECS degree project courses – as these names are taken from the actual list of
examiners associated with real courses. Note that the student names were chosen
randomly but to have a different initial letter so as to make testing simpler.
Given the information in the table above, a program can retrieve David’s
program data via an HTTP request to the path
http://canvas.docker/api/v1/users/sis_user_id:z4/custom_data/program_of_
study.
Implementation | 63
4.4.3 Primary Initializations for REST API calls
The dynamic survey application was written in Ruby by Prof. Maguire and uses the
Sinatra* framework.
The first step was to include the required gems into the ruby program. The first
gem included is the JSON API, which facilitates working with JSON (in this case,
this functionality is used to read a file that is encoded in JSON format). The other
gem is the httparty API, which contains classes and methods relevant to working
with HTTP requests. This is used to access Canvas via the Canvas RESTful API.
These two gems are included in the first two lines of Listing 4-17.
The second step was to retrieve a user’s access token. In this case, the access
token used is the one associated with a user who has administrative rights to access
all of Canvas. Retrieving the access token (stored in a configuration file) will allow
RESTful API calls to retrieve information or store information into the running
Canvas instance [48]. The code to retrieve the access token and the host name of
the Canvas instance are also shown in Listing 4-17.
Listing 4-17: Primary initializations made for REST API calls
require 'json'
require 'httparty'
config = JSON.parse(File.read('config.json'))
access_token = config['canvas']['access_token']
host = config['canvas']['host']
$header = {'Authorization': 'Bearer ' "#{access_token}", 'Content-Type': 'application/json', 'Accept': 'application/json'}
In the listing above, “File.read('config.json')” returns the contents of the
file config.json as a string. Next JSON.parse converts the JSON encoded string into
a Ruby structure (specifically a Ruby Hash). This ruby structure is saved in the
variable config.
From the variable config both the access token and host name were retrieved,
and these were stored in the variable ‘access_token’ and the variable ‘host’
(respectively). The variable ‘header’ defines a header for the HTTP requests. The
access token is stored in this header and will be included in the header of all
requests to the Canvas API.
4.4.4 Initializations for LTI Integration
In order for the program to utilize the LTI standard to communicate, the first
step is to include two additional gems into the program: ‘oauth’ and
‘oauth/request_proxy/rack_request’. These two gems are required for the
* Sinatra is a light-weight framework written in Ruby. Unlike Ruby on Rails, Sinatra does not follow the Model-View-Controller (MVC) architecture and it allows for the development of web applications with a much lower effort [47].
64 | Implementation
authorization-specific functionalities of an LTI-based service provider*. The
‘sinatra’ gem allows the ruby program to use the Sinatra framework. The next
step is to define some global variables. All these steps are shown in Listing 4-18.
Listing 4-18: Variables initialized for LTI-specific functionalities
#Gems require 'oauth' require 'oauth/request_proxy/rack_request' require ‘sinatra’ #Global variables $oauth_key = "test" $oauth_secret = "secret" disable :protection enable :sessions
The lines ‘$oauth_key = “test”’ and ‘$oauth_secret = “secret”’ hardcode
the key and secret that will be used when embedding the program into Canvas as an
assignment. These values would be inappropriate for a real LTI tool and are only
used for testing purposes.
By default, Sinatra expects to set x-frame options. For testing purposes, this is
disabled with the line disable :protection. The line enable :sessions is added so
that the program can remember information between HTTP requests when a given
user is taking part in the quiz.
The next step is to make further configurations in the route by which the
program is launched for a new user’s interaction with the quiz. For convenience,
this route was named ‘/start’ and is shown in Listing 4-19.
Listing 4-19: Configurations made to ensure the tool follows the LTI standard
post '/start' do #First section of the ‘/start’ route begin signature = OAuth::Signature.build(request, :consumer_secret => $oauth_secret) signature.verify() or raise OAuth::Unauthorized rescue OAuth::Signature::UnknownSignatureMethod, OAuth::Unauthorized return %{unauthorized attempt. make sure you used the consumer secret "#{$oauth_secret}"} end #Second section of the ‘/start’ route unless params['lis_outcome_service_url'] && params['lis_result_sourcedid'] return %{It looks like this LTI tool wasn't launched as an assignment, or you are trying to take it as a teacher rather than as a student. Make sure to set up an external tool assignment as outlined <a target="_blank" href="https://github.com/instructure/lti_example">in the README</a> for this
* Oauth is an industry standard protocol for authentication and it widely used.
Implementation | 65
example.} end #Third and final section of the ‘/start’ route %w(lis_outcome_service_url lis_result_sourcedid lis_person_name_full lis_person_contact_email_primary lis_person_sourcedid custom_canvas_course_id custom_canvas_user_id ).each { |v| session[v] = params[v] } redirect to("/getProgramData") end
The first section of this route verifies that the correct authorization key is used
for the consumer secret. If this verification fails, the program simply returns a
message that notifies the user that he or she is making an unauthorized attempt to
embed the program into Canvas.
However, if the verification is successful, the program continues to the second
section of the route. The purpose of this section is to verify that when the LTI tool
(i.e. this program) is launched by a user, it is being done so from the Canvas LMS.
In this section, the parameter 'lis_outcome_service_url' corresponds to the Tool
Consumer (TC) URL for reporting a grade for an assignment, while the second
parameter 'lis_result_sourcedid' acts as an identifier that maps the assignment
grade to the student taking part in the assignment. If the program is launched from
an application or service other than Canvas, then these parameters will not be
provided and the verification will fail. Both 'lis_outcome_service_url' and
'lis_result_sourcedid' correspond to LTI LIS services.
The next section of the route stores the relevant parameters as key-value pairs
in the user’s session. Note that ‘lis_person_sourcedid’ corresponds to an LTI
LIS service and stores the SIS ID for the user (i.e. the student using the tool). The
corresponding value for each of these keys can be retrieved by simply making a
reference to ‘session[‘key’]’, where key represents the name of the key. For
instance, retrieving the user’s SIS ID requires a reference to
‘session[lis_person_sourceid]’. Once the code in this section is executed,
there are no additional LTI-based configurations to be made. Now the route simply
redirects the user to the ‘/getProgramData’ route of the LTI tool, which is
responsible for making HTTP requests to retrieve the user’s program of study – for
the user accessing the dynamic quiz.
4.4.5 Retrieval a Student’s Program of Study
During the tests, the primary route responsible for retrieving a student’s program
of study is ‘/getProgramData’*. According to the Canvas API documentation, when
using the Canvas API to both store or retrieve custom data (with PUT/POST or
GET requests respectively), the body of the HTTP request must contain not only
the custom data in question, but also the namespace parameter (ns). The purpose
of this parameter is to prevent collisions of custom data between different
* As mentioned earlier, the program data that was assigned to every student was also custom-made.
66 | Implementation
applications. This parameter is a requirement for all HTTP requests concerning
custom data. Hence, the first step in the ‘/getProgramData’ route was the
declaration of the namespace parameter in the body of the soon-to-be-made GET
request (as shown in Listing 4-20).
Listing 4-20: The namespace parameter stored in the body of the GET request
@payload={"ns" => "se.kth.canvas-app.program_of_study"}
Next, the tool tests to ensure that the SIS ID is linked to the user. If the test fails,
then the user is presented with a message informing them that the SIS ID is not
associated with any user. This is shown in Listing 4-21.
Listing 4-21: Test to ensure SIS ID is linked to the user
unless session['lis_person_sourcedid'].length > 0 puts "session['lis_person_sourcedid']=#{session['lis_person_sourcedid']}" return %{It looks like there is no usr_sis_id for this user.} end
The URL from which the custom program data for the user can now be accessed
is stored in a variable, as shown in Listing 4-22.
Listing 4-22: URL for GET request
@url_to_use = ''http://#{$canvas_host}/api/v1/users/sis_user_id:#{session['lis_person_sourcedid']}//custom_data/program_of_study''
The actual GET request is made to the URL with the body and the header that
were stored earlier, and the result is stored in a variable named ‘@getResponse’ (as
shown in Listing 4-23).
Listing 4-23: The ‘@getResponse’ variable
@getResponse = HTTParty.get(@url_to_use,:body => @payload.to_json, :headers => $header )
Next, the application checks if the user has any study program data available by
looking at the HTTP status code of the GET response. If the status code is over 200
(i.e. the request is not OK), this means that no study program was retrieved by the
GET request and this implies that the user has no program data available. In this
case, the user will be directed to the ‘getUserProgram’ route. This test is shown in
Listing 4-24.
Listing 4-24: Test to check if the user is part of a study program
if @getResponse.code > 200 puts("The user had no program data stored for them. They will have to select their program.") redirect to("/getUserProgram") end
Implementation | 67
The ‘getUserProgram’ route is shown in Listing 4-26. This route will prompt
the user to indicate which program they are in. If, on the other hand, the GET
request in Listing 4-23 succeeded, then the user has program data available and is
part of a study program. In this case, the next step is to determine if the user is in
one or more study programs. This process is shown in Listing 4-25.
Listing 4-25: Checking if the user is taking one or more study programs
@return_data=@getResponse['data']['programs']
#puts "getResponse['data']['programs'] is #{@return_data}"
@class_of_return_data=@return_data.class
#puts "class of return_data is #{@class_of_return_data}"
@program_codes=Array.new
@return_data.each do |program|
@program_code=program['code']
@program_codes << @program_code
end
if @program_codes.length == 1
session['program_code']=@program_codes[0]
puts("There is a single program code: #{session['program_code']}")
else
# there is little support for students in multiple programs (yet1)
session['program_codes']=@program_codes
puts("There are multple program codes:
#{session['program_codes']}")
end
redirect to("/getGeneralData")
As seen in Listing 4-25, the route first retrieves all of the user’s programs from
the GET request (which were initially stored as custom data) and saves them into
the array ‘return_data’. Next, it iterates through ‘return_data’ and during each
iteration retrieves the program code of the program being checked, and stores the
program code into the array ‘program_codes’. Once the route completes iterating
through ‘program_codes’, it proceeds to check if the user is part of one program or
multiple programs by looking at the length of the ‘program_codes’ array. An array
length of 1 would indicate that the user is a part of only one program and in this
case, the route simply stores the program code (which is the first and only content
of the array) into the user’s session. Otherwise, the route stores the entire array into
68 | Implementation
the user’s session. Finally, the route ends by redirecting to the ‘/getGeneralData’
route, which is described in Section 4.4.6.
Listing 4-26: The /getUserProgram route
get "/getUserProgram" do @program_options='' @programs=$programs_in_the_school_with_titles.sort puts("@programs is #{@programs}") # note that each "program" value is of the form ["CDATE", {"owner"=>"EECS", "title_en"=>"Degree Programme in Computer Science and Engineering", "title_sv"=>"Civilingenjörsutbildning i datateknik"}] @programs.each do |program| #puts("program is #{program}") if program.length > 0 @program_name=program[0] puts("@program_name is #{@program_name}") @title=$programs_in_the_school_with_titles[@program_name]['title_en'] @title_s=$programs_in_the_school_with_titles[@program_name]['title_sv'] #puts("title is #{@title}") #puts("title is #{@title_s}") @program_options=@program_options+'<option value="'+@program_name+'">'+@program_name+': '+@title+' | '+@title_s+'</option>' end end puts("program_options is #{@program_options}") <<-HTML <html > <head ><title ><span lang="en">Which program of study are you in?</span> | <span lang="sv">Vilket studieprogram är du i?</span></title ></head > <body > <form action="/gotUsersProgram" method="post"> <h2>Which program of study are you in?</span> | <span lang="sv">Vilket studieprogram är du i?</span></h2> <select if="program_code" name="program_code"> #{@program_options} </select> <br><input type='submit' value='Submit' /> </form> </body > </html > HTML end
This route starts by sorting all the values in the global hash
‘$programs_in_the_school_with_titles’. Note that in order for this to work,
some additional code is required. First, the temporary hash
‘programs_in_the_school_with_titles’ must first be declared and filled with
data at the beginning of the program (as shown in Listing 4-27).
Implementation | 69
Listing 4-27: The programs_in_the_school_with_titles variable
if $with_contraints all_data=JSON.parse(File.read('course-data-EECS-cycle-2c.json')) else all_data=JSON.parse(File.read('course-data-EECS-cycle-2.json')) end cycle_number=all_data['cycle_number'] programs_in_the_school_with_titles=all_data['programs_in_the_school_with_titles']
As was shown in Listing 4-27, the program relies on a reference file which is
written in JSON. The reference file contains all the data relating to the study
programs, the degree project courses, and the examiners. If the toggle
‘$with_contraints’ is set to ‘true’, then the program reads from the ‘course-data-EECS-cycle-2c.json’ file and stores the information in the ‘all_data’
hash, otherwise, it reads from the ‘course-data-EECS-cycle-2.json’ file and
stores the information in the ‘all_data’ hash*. From this point forward, the
‘all_data’ hash can be used to retrieve information that was stored in the
reference file. Next, the cycle number specified in the reference file is retrieved
from the ‘all_data’ hash by using ‘cycle_number’ as a key. Finally, all the study
programs are accessed using the key 'programs_in_the_school_with_titles'
and stored in the temporary hash ‘programs_in_the_school_with_titles’. An
additional point to note here is that each such program is stored in the form shown
in Listing 4-28.
Listing 4-28: Format of each “program” in programs_in_the_school_with_titles
["CDATE", {"owner"=>"EECS", "title_en"=>"Degree Programme in Computer Science and Engineering", "title_sv"=>"Civilingenjörsutbildning i datateknik"}]
The global hash ‘$programs_in_the_school_with_titles’ is then initialized
as shown in Listing 4-29.
Listing 4-29: Initializing the global variable '$programs_in_the_school_with_titles'
$programs_in_the_school_with_titles=programs_in_cycle(cycle_number,
programs_in_the_school_with_titles)
As seen in Listing 4-29, the global array is passed in a call to the helper function
‘programs_in_cycle’, which takes the cycle number and the temporary array
‘programs_in_the_school_with_titles’. This helper function is shown in
Listing 4-30.
* This code assumes that the relevant school is KTH’s School of Electrical Engineering and Computer Science (EECS) and that the quiz is used for the 2nd cycle degree project courses (i.e., Master’s degree projects). A production version of the program would need to get these values as parameters when invoked via the LTI interface.
70 | Implementation
Listing 4-30: The 'programs_in_cycle' helper function
def programs_in_cycle(cycle_number, programs)
cycle=cycle_number.to_i
#puts("in programs_in_cycle cycle is #{cycle}")
relevant_programs={}
#puts("programs is #{programs}")
programs.each do |prog_code, prog_value| # you have to iterate this way as programs is a hash
#puts("prog_code is #{prog_code}")
#puts("prog_value is #{prog_value}")
@program_name = prog_code
#puts("@program_name is #{@program_name}")
@credits = programs[@program_name]['credits'].to_i
#puts("@credits is #{@credits}")
@title_sv = programs[@program_name]['title_sv']
if (@credits >= 270) and ((cycle == 1) or (cycle == 2))
#puts("Found Civ. ing. program")
relevant_programs[prog_code]=prog_value
elsif (@credits == 180) and (cycle == 1)
#puts("Found Hög. ing. program")
relevant_programs[prog_code]=prog_value
elsif (@credits == 120) and (cycle == 2)
relevant_programs[prog_code]=prog_value
elsif (@credits == 30) and (cycle == 0)
relevant_programs[prog_code]=prog_value
elsif (@credits == 60) and (cycle == 0) and (@title_sv.include? 'Tekniskt basår')
relevant_programs[prog_code]=prog_value
elsif (@credits == 60) and (cycle == 0) and (@title_sv.include? 'Tekniskt basår')
relevant_programs[prog_code]=prog_value
elsif (@credits == 60) and (cycle == 2) and (@title_sv.include? 'Magisterprogram')
relevant_programs[prog_code]=prog_value
else
# nothing to do
end
end
return relevant_programs
end
Implementation | 71
The main purpose of the helper function is to filter the study programs in the
‘programs_in_the_school_with_titles’ array and retrieve only the study
programs that meet certain criteria in terms of the number of credits, the cycle
number, and the contents of their corresponding Swedish title. The relevant study
programs are stored in the hash ‘relevant_programs’ as key-value pairs, where
the key is the program code of each study program and the value represents the
program information such as owner, title in English, title in Swedish, and the
credits (where each of these pieces of information also act as keys to their
corresponding values). The helper function ends by returning the filtered hash of
courses, which is stored in the global hash
‘$programs_in_the_school_with_titles’.
Referring back to Listing 4-26, once the tool has a sorted array of “program”
values, for each program value, it stores the corresponding program name in
English and in Swedish into appropriate variables and turns each program value
into a value attribute for an HTML option tag. Once this is done, the tool renders
an HTML form that asks the user which program he or she belongs to. All the
program codes (each corresponding to a program value) are presented in a drop-
down list (as each program value was converted to a value attribute for the HTML
option tag earlier). Furthermore, the parameter name for these options is
program_code so that whatever the user selects from the drop-down list can be
accessed with params[‘program_code’] in a subsequent route that processes this
form. Once the user selects the appropriate program from the drop-down list and
submits the form, the tool redirects to the ‘/gotUsersProgram’ route, shown in
Listing 4-31. As one can see in this listing, the HTML form in Listing 4-26 is only
presented to the user if the tool fails to detect the study program that the user is
taking part in. This is one of the many instances in this application where the
response to a request via the Canvas API determines which questions the user is
asked. Given that the user’s study program was unknown (based on the response to
the HTTP GET request), this HTML form will be the first page the user will see
when taking part in this quiz. However, if the user’s program of study is known,
then this HTML form would not have been rendered and the user would not see
this page. In this latter case, the first page the user would see is the HTML form
rendered in the ‘getGeneralData’ route.
As shown in Listing 4-31, the ‘/gotUsersProgram’ route first accesses the value
selected by the user from the drop-down list (from Listing 4-26) with
params[‘program_code’] and checks if it is non-existent or empty. If one of these
conditions are met, this indicates that the user submitted the HTML form without
selecting a program from the drop-down list. In this case, the user will simply be
redirected to the ‘/getUsersProgram’ route to be prompted to select his or her
study program from the drop-down list. However, if the user selected a program of
study from the list, then the tool will simply save the selected program code into the
user’s session (under the key named 'program_code') and redirect the user to the
72 | Implementation
‘/getGeneralData’ route, which is responsible for obtaining some general data
from the user regarding his or her planned degree project.
Listing 4-31: The /gotUsersProgram route
post "/gotUsersProgram" do program_code=params['program_code'] if !program_code || program_code.empty? redirect to("/getUserProgram") end session['program_code']=program_code redirect to("/getGeneralData") end
4.4.6 Retrieving Students’ General Data
The first step taken by the ‘/getGeneralData’ route is to retrieve the user’s
program code from the user’s session. Since this value was initially inserted into the
user’s session under the key named 'program_code', it can be retrieved in the same
fashion, as shown in Listing 4-32.
Listing 4-32: Retrieval of user’s program code
@program_code=session['program_code']
Next, the route checks whether the student is part of a first cycle program or a
second cycle program and saves the appropriate cycle number into a variable. Then
it initializes the minimum starting date the user can select for his or her degree
project and the maximum starting date that the user can select for his or her degree
project. This is shown in Listing 4-33.
Listing 4-33: Checking study cycle and initializing minimum and maximum possible start
dates for the degree project
if cycle_number == "1" @cycle_number_ordinal='1<sup>st</sup>' else @cycle_number_ordinal='2<sup>nd</sup>' end planned_start_today=Time.new planned_start_min=planned_start_today planned_start_max=planned_start_today + (11*30*24*60*60)
It is worth mentioning that using the ‘Time’ class requires the inclusion of the
‘date’ gem into the ruby program. Note that there is upper bound on the starting
time of 11 months from the current data. This is an arbitrary limit, but it helps
ensure that the student is starting in this year rather than the next year. In the ideal
case, the check should be made with respect to the term or semester, so that the
student is registered into the correct period for their degree project.
The route then checks if the user is part of the TIVNM study program. If the
user is part of the TIVNM study program, then the route saves a small piece of
Implementation | 73
HTML code in a variable named ‘@graded_or_ungraded_question’. The HTML
code contains a message to inform the user that all students from the TIVNM
program are required to have the A-F grading system for their degree project.
However, if the user is not a part of the TIVNM study program, then the route saves
a different piece of HTML code in the ‘@graded_or_ungraded_question’ variable.
This HTML code prompts the user to select his or her choice of grading system
(A-F or Pass/Fail) for the degree project. As in the code in Listing 4-26, whatever
the user selects is given the parameter name grading_scale, so that later it can be
accessed with params[‘grading_scale’] in the route that processes this form.
This is shown in Listing 4-34. Note that the text that the user sees will be in both
English and Swedish.
Listing 4-34: Initializing the contents of the ‘@graded_or_ungraded_question’ variable
depending on the user’s program
if %w(TIVNM ).include? @program_code @graded_or_ungraded_question='<p><span lan="en">All students in ' + @program_code + ' must have A-F grading.</span>/<span lan="sv">Alla elever i ' + @program_code + ' måste ha A-F-gradering.</p>' else @graded_or_ungraded_question='<h2><span lang="en">Grading scale</span>|<span lang="sv">Betygsskala</span></h2> <p><span lang="en">Do you wish an A-F grade, rather than the default P/F (i.e. Pass/Fail) grade for your degree project?</span> | <span lang="sv">Vill du ha ett betygsatt exjobb (A-F), i stället för ett vanligt med bara P/F (Pass/Fail)?</span></p> <span> <span> <input type="radio" name="grading_scale" value="grading_scale_AF"/> <span lan="en">Grade A-F</span> | <span lang="sv">Betygsatt exjobb (A-F)</span><br> </span> <span> <input type="radio" name="grading_scale" value="grading_scale_PF" checked="checked" autofocus required="required"/> <span lang="en">Pass/Fail (standard)</span> | <span lang="sv">Godkänd eller underkänd (standard)</span> </span> </span>' end
These HTML codes are rendered in this route based upon the HTML code saved
in the variable ‘@graded_or_ungraded_question’. It should also be noted that
between the two variations of the ‘@graded_or_ungraded_question’ variable,
only in the second variation is the literal ‘grading_scale’ saved as a key in the
params hash when the user fills in the form. If the user was from the TIVNM
program, the literal grading_scale is not saved as a key in the hash and as a
result, any future reference to params[‘grading_scale’] would fail. Hence,
before any reference to params[‘grading_scale’] is made, there must be a
conditional statement embedded in the code to check whether the reference
actually exists.
74 | Implementation
An alternative solution would be to explicitly return the value without
displaying the input (by setting the input type to hidden) as shown in Listing 4-36.
Listing 4-35: Initializing the contents of the ‘@graded_or_ungraded_question’ variable
depending on the user’s program
if %w(TIVNM ).include? @program_code @graded_or_ungraded_question='<p><span lan="en">All students in ' + @program_code + ' must have A-F grading.</span>/<span lan="sv">Alla elever i ' + @program_code + ' måste ha A-F-gradering.</p><input type="hidden" name="grading_scale" value="grading_scale_AF"/>' else @graded_or_ungraded_question='<h2><span lang="en">Grading scale</span>|<span lang="sv">Betygsskala</span></h2> <p><span lang="en">Do you wish an A-F grade, rather than the default P/F (i.e. Pass/Fail) grade for your degree project?</span> | <span lang="sv">Vill du ha ett betygsatt exjobb (A-F), i stället för ett vanligt med bara P/F (Pass/Fail)?</span></p> <span> <span> <input type="radio" name="grading_scale" value="grading_scale_AF"/> <span lan="en">Grade A-F</span> | <span lang="sv">Betygsatt exjobb (A-F)</span><br> </span> <span> <input type="radio" name="grading_scale" value="grading_scale_PF" checked="checked" autofocus required="required"/> <span lang="en">Pass/Fail (standard)</span> | <span lang="sv">Godkänd eller underkänd (standard)</span> </span> </span>' end
The route then generates an HTML form that prompts the user to enter
information regarding DiVA, specifically whether the user wants DiVA to make the
full text of the user’s final report available (or to simply archive it) and additional
information required to start the project, including tentative title of the user’s
degree project, name of the company where the user’s degree project is being
conducted (if the user is doing their project at a company), the country where the
user wishes to do the degree project, the planned start date of the degree project,
and some other general information. For these questions, each answer selected by
the user is given a parameter name, so that they can be accessed from a subsequent
route from the params hash, as was done in the code in Listing 4-31. The final
piece of information displayed to the user by this HTML form is the contents of the
‘@graded_or_ungraded_question’ variable from Listing 4-36. If the user is part of
the TIVNM program, then this information is simply a message to inform the user
that only the A-F grading system applies to his or her degree project. Otherwise,
this information is another question that prompts the user to select his or her
choice of the grading system. This is another instance in the application where the
contents a question presented to the user depends on either the user’s answer to a
question in a previous HTML form or data retrieved from Canvas. The code for the
Implementation | 75
entire HTML form is shown in Listing 4-36. Once the user submits the HTML
form, he or she is redirected to the ‘/assessment’ route.
Listing 4-36: HTML form (code) for obtaining general information from the user
<<-HTML <html> <head><title>Dynamic survey for replacing UT-EXAR form</title></head> <body> <h1>Application for a #{@cycle_number_ordinal} cycle degree project</h1> <form action="/assessment" method="post"> <p><span lang="en">As a student in the #{$programs_in_the_school_with_titles[@program_code]['title_en']} (#{@program_code}) you need to complete a degree project. This survey collects some data to help admininster your project and to you register for the correct course and be assigned an appropriate examiner.</span> | <span lang="sv">Som student i #{$programs_in_the_school_with_titles[@program_code]['title_sv']} (#{@program_code}) måste du slutföra ett examensarbete. Denna undersökning samlar in några data för att hjälpa till att administrera ditt projekt och att du registrerar dig för rätt kurs och tilldelas en lämplig granskare.</span></p> <h2><span lang="en">Full text in DiVA</span> | <span lang="sv">Fulltext i DiVA</span></h2> <p><span lang="en">Do you give KTH permission to make the full text of your final report available via DiVA?</span> | <span lang="sv">Ger du KTH tillstånd att publicera hela din slutliga exjobbsrapport elektroniskt i databasen DiVA?</span></p> <p><strong><span lang="en">Note that in all cases the report is public and KTH must provide a copy to anyone on request.</span> | <span lang="sv">Observera att din slutliga exjobbsrapport alltid är offentlig, och att KTH alltid måste tillhandahålla en kopia om någon begär det.</span></strong></p> <span> <span> <input type="radio" name="diva_permission" value="yes_to_diva" checked="checked" autofocus required="required"/> <span lang="en">I accept publication via DiVA</span> | <span lang="sv">Jag godkänner publicering via DiVA</span><br> </span> <span> <input type="radio" name="diva_permission" value="no_to_diva" /> <span lang="en">I do not accept publication via DiVA</span> | <span lang="sv">Jag godkänner inte publicering via DiVA</span> </span> </span> <h2><span lang="en">Tentative title</span> | <span lang="sv">Preliminär titel</span></h2> <input name='Tentative_title' type='text' width='1000' id='Tentative_title' /> <h2><span lang="en">At a company, indicate name</span> | <span lang="sv">På företag, ange vilket</span></h2> <input name='company' type='text' width='1000' id='company' /> <h2><span lang="en">Outside Sweden, indicate Country</span> | <span lang="sv">Utomlands, ange land</span></h2> <select id="country_code" name="country_code"> <option value="">--Please choose a contry code | Vänligen välj en landskod--</option> <option value="AF">Afghanistan</option>
76 | Implementation
<option value="AX">Åland Islands</option> <option value="AL">Albania</option> <option value="DZ">Algeria</option> <option value="AS">American Samoa</option> <option value="AD">Andorra</option> <option value="AO">Angola</option> <option value="AI">Anguilla</option> <option value="AQ">Antarctica</option> … <option value="VG">Virgin Islands, British</option> <option value="VI">Virgin Islands, U.S.</option> <option value="WF">Wallis and Futuna</option> <option value="EH">Western Sahara</option> <option value="YE">Yemen</option> <option value="ZM">Zambia</option> <option value="ZW">Zimbabwe</option> </select> <h2><span lang="en">At another university</span> | <span lang="sv">På annan högskola</span></h2> <input name='university' type='text' width='1000' id='university' /> <h2><span lang="en">Contact</span> | <span lang="sv">Kontaktinformation</span></h2> <p><span lang="en">Enter the name and contact details of your contact at a company, other university, etc.</span> | <span lang="sv">Ange namn, e-postadress och annan kontaktinformation för din kontaktperson vid företaget, det andra universitetet, eller motsvarande.</span></p> <input name='contact' type='text' width='1000' id='contact' /> <h2><span lang="en">Planned start</span>/<span lang="sv">Startdatum</span></h2> <label for="start">Date/Datum:</label> <input type="date" id="start" name=planned_start value=#{planned_start_today} min=#{planned_start_min} max=#{planned_start_max}> #{@graded_or_ungraded_question} <br><input type='submit' value='Submit' /> </form> </body> </html> HTML
Implementation | 77
Listing 4-37 shows the first section of the ‘/assessment’ route. This section of
the route primarily concerns retrieving the answers that the user selected in the
form which was rendered by the ‘/getGeneralData’ route and saving each answer
into a unique variable and storing them into the session. As in the code shown in
Listing 4-31, this is done with Ruby’s ‘params’ hash.
Listing 4-37: Retrieving and saving the general data entered by the student
The next section of the route, shown in Listing 4-38, initializes the grading
system that will be used by the user’s degree project. It first checks if the key
‘grading_scale’ exists in the ‘params’ hash. If it does exist (which would be the
case if the user is not from the TIVNM program), the route simply retrieves the
grading system selected by the user with params['grading_scale'] and saves it
in the Ruby variable @grading_scale. If the key does not exist (which would be
the case if the user is from the TIVNM program), the Ruby variable
@grading_scale is simply initialized to the literal 'grading_scale_AF'. The
route then saves the variable into the user’s session, under the key name
'grading_scale'.
@diva_permission = params['diva_permission'] puts "diva_permission is #{@diva_permission}" session['diva_permission']=@diva_permission @tentative_title = params['Tentative_title'] puts "Tentative_title is #{@tentative_title}" session['Tentative_title']=@tentative_title @company = params['company'] puts "company is #{@company}" session['company']=@company @country_code = params['country_code'] puts("country_code is #{@country_code}") session['country_code']=@country_code @university = params['university'] puts "university is #{@university}" session['university']=@university @contact = params['contact'] puts "contact is #{@contact}" session['contact']=@contact @planned_start = params['planned_start'] puts("planned_start is #{@planned_start}") session['planned_start']=@planned_start
78 | Implementation
Listing 4-38: Initializing the @grading_scale variable
if params.has_key?('grading_scale') @grading_scale = params['grading_scale'] else @grading_scale = 'grading_scale_AF' end session['grading_scale']=@grading_scale
The last section of the route, shown in Listing 4-39, simply checks the value of
the @grading_scale variable and depending on the value, redirects the user to a
new route. If @grading_scale equals 'grading_scale_AF', then the user is
redirected to the ‘/grading_scale_AF’ route, otherwise the user is redirected to
the ‘/grading_scale_PF’ route. These routes will display the different choices of
courses codes that the user can select from.
Listing 4-39: Redirect user to new route depending on the value of @grading_scale
if @grading_scale == 'grading_scale_AF' redirect to("/grading_scale_AF") else redirect to("/grading_scale_PF") end
4.4.7 Displaying Course Options
KTH offers several degree project courses and the degree project courses that a
given student can choose from depend on the student’s program of study and
choice of grading scale. This overall mechanism is coded in the
‘/grading_scale_AF’ route and the ‘/grading_scale_PF’ route. Before these
routes are utilized, some additional variables needed to be initialized (at the start of
the program). These variables are shown in Listing 4-40. This information was
previously collected from a course and program planning (Swedish: Kurs- och
programplanering) (KOPPS) database and humans and then stored in the JSON
formatted filed described in Section 4.4.5, see specifically Listing 4-27.
Listing 4-40: Required initializations that the “/grading_scale_AF” and
“grading_scale_PF” routes depend on
AF_courses=all_data['AF_courses'] PF_courses=all_data['PF_courses'] relevant_courses_English=all_data['relevant_courses_English'] relevant_courses_Swedish=all_data['relevant_courses_Swedish'] if $with_contraints $PF_course_codes_by_program=all_data['PF_course_codes_by_program'] #puts("$PF_course_codes_by_program is #{$PF_course_codes_by_program}") $AF_course_codes_by_program=all_data['AF_course_codes_by_program'] #puts("$AF_course_codes_by_program is #{$AF_course_codes_by_program}") end
Implementation | 79
As shown in Listing 4-40, the array ‘AF_courses’ stores a list all the degree
project courses (by course code) at KTH with the A-F grading scale, whereas the
array ‘PF_courses’ stores a list of all the degree project courses (by course code) at
KTH with the Pass/Fail grading scale. The hash ‘relevant_courses_English’
stores all the degree project courses at KTH along with their respective course
information in English, whereas the hash ‘relevant_courses_Swedish’ stores all
the degree project courses at KTH along with their respective course information in
Swedish. Note the ‘all_data’ hash (whose creation is shown in Listing 4-27) is
used to retrieve the courses from the information that was stored in the JSON file.
Each value in the ‘relevant_courses_English’ is in the format shown in Listing
4-41.
Listing 4-41: Format of each value in relevant_courses_English
["DA235X": {"code": "DA235X", "title": "Degree Project in Computer Science and Engineering, specializing in Industrial Management, Second Cycle", "href": "https://www.kth.se/student/kurser/kurs/DA235X?l=en", "info": "<p>The aim of the degree project (1) is for the student to apply and deepen knowledge, understanding, abilities and approach within the context of the education. The degree project should be carried out at the end of the education and provide a specialised study and synthesis of earlier acquired knowledge. In the degree project is emphasised both the technical/scientific content and method knowledge.</p><p>1] Also designated independent project</p>", "credits": "30.0", "level": "Second cycle", "state": "ESTABLISHED", "dept_code": "JH", "department": "EECS/Computer Science", "cycle": "2", "subject": "Degree Project in Computer Science and Engineering”}]
Each value in the relevant_courses_Swedish hash follows a similar format,
as shown in Listing 4-42.
Listing 4-42: Format of each value in relevant_courses_Swedish
["DA235X": {"code": "DA235X", "title": "Examensarbete i datalogi och datateknik med inriktning mot industriell ekonomi, avancerad niv\u00e5", "href": "https://www.kth.se/student/kurser/kurs/DA235X", "info": "<p>Examensarbetet[1] syftar till att studenten skall tillämpa och fördjupa kunskaper, förståelse, förmågor och förhållningssätt inom utbildningens sammanhang. Examensarbetet skall ligga i slutet av utbildningen och innebära en fördjupning och syntes av tidigare förvärvade kunskaper. I examensarbetet betonas både det tekniska/naturvetenskapliga innehållet och metodkunskaper.</p><p>1] Också benämnt självständigt arbete</p>", "credits": "30,0", "level": "Avancerad niv\u00e5", "state": "ESTABLISHED", "dept_code": "JH", "department": "EECS/Datavetenskap", "cycle": "2", "subject": "datalogi och datateknik med inriktning mot industriell ekonomi”}
The variable PF_course_codes_by_program contains course codes for courses
that are graded Pass/Fail, while the variable AF_course_codes_by_program
contains course codes for courses that utilize the A-F grading scale.
The ‘/grading_scale_AF’ route contains three sections of code. In the first
section of the route (shown in Listing 4-43), all the degree project courses with the
A-F grading scale are sorted, the program code is retrieved from the user’s session,
80 | Implementation
and a call to a function named ‘filter_courses_for_a_program’ is made. This
function takes 4 arguments: the user’s program code, the cycle number, the grading
scale (which, in this case, is AF) and the sorted list of courses with the A-F grading
scale.
Listing 4-43: First section of the ‘/grading_scale_AF’ route
@courses=AF_courses.sort puts("courses is #{ @courses}") @program_code=session['program_code'] @courses = filter_courses_for_a_program(@program_code, cycle_number, 'AF', @courses)
The ‘filter_courses_for_a_program’ function is shown in Listing 4-44. This
function retrieves all the degree project courses with the selected grading scale
(which, in this case, is ‘AF’) that are available for the user’s study program and cycle
number and stores them in the variable ‘relevant_course_codes’. Next, for every
course in the sorted list of courses, the function checks if the course exists in
‘relevant_course_codes’ and if it does, the function stores the course in the array
‘relevant_courses’ and once the iterations are complete, it returns the array.
Otherwise, the function simply returns the large sorted list of courses without any
filtration.
Listing 4-44: The ‘filter_courses_for_a_program’ function
def filter_courses_for_a_program(program_code, cycle_number, grading_scale, courses) cycle_code='cycle'+cycle_number puts("cycle_code is #{cycle_code}") relevant_courses=[] if $with_contraints if grading_scale == 'AF' relevant_course_codes=$AF_course_codes_by_program[cycle_code][program_code] #relevant_course_codes is all the degree project courses for a certain program else relevant_course_codes=$PF_course_codes_by_program[cycle_code][program_code] end if not relevant_course_codes puts("no relevant course codes found for #{program_code} in cycle #{cycle_number}") return courses # if there are no course codes, then do not filter end puts("relevant course_codes for #{program_code} in cycle #{cycle_number} are #{relevant_course_codes}") courses.each do |course_code| # you have to iterate this way as programs is a hash
Implementation | 81
puts("course_code is #{course_code}") if relevant_course_codes.include?(course_code) relevant_courses << course_code end end puts("relevant #{grading_scale} courses for #{program_code} in cycle #{cycle_number} are #{relevant_courses}") if relevant_courses.length > 0 return relevant_courses else return courses # if there are no course codes left, then do not filter end else # if not $with_contraints do not filter return courses end end
The return value from the ‘filter_courses_for_a_program’ function is
stored in the ‘@courses’ variable of the ‘/grading_scale_AF’ route. In this case,
this variable will store only the degree project courses that are suitable for the
user’s study program and cycle number and use the A-F grading scale.
In the second section of the ‘/grading_scale_AF’ route, the function iterates
through all the courses stored in the ‘@courses’ array and does the following in
each iteration (see Listing 4-45):
1. Retrieve the course title from the ‘relevant_courses_English’ hash
2. Retrieve the course title in Swedish from the ‘relevant_courses_Swedish’
hash
3. Retrieve the number of obtainable credits for the course from the
‘relevant_courses_English’ hash
4. Turn each course code into a value attribute for HTML’s option tag (along with
the course titles in English and Swedish and the number of obtainable credits for
the course), so that all the courses stored in the ‘@courses’ variable can be
displayed in a drop-down list as a selectable option
Listing 4-45: The second section of the ‘/grading_scale_AF’ route
@course_options='' @courses.each do |course| @title=relevant_courses_English[course]['title'] @title_s=relevant_courses_Swedish[course]['title'] @credits=relevant_courses_English[course]['credits'] @course_options=@course_options+'<option value="'+course+'">'+course+': '+@credits+' '+@title+' | '+@title_s+'</option>' end
82 | Implementation
In the final section of the ‘/grading_scale_AF’ route, shown Listing 4-46, the
function renders an HTML form that prompts the user to select his or her desired
choice of degree project course. Only the courses that are suitable for the user’s
program of study and use the A-F grading scale are presented in a drop-down list.
Furthermore, this drop-down list is given the parameter name ‘selected_course’,
so that whatever the user selects from the drop-down list can be accessed in the
subsequent route via params[‘selected_course’]. Once the user selects a course
and submits the form, he or she is directed to the ‘/Examiner’ route of the tool.
Listing 4-46: Final section of the ‘/grading_scale_AF’ route
<<-HTML <html > <head ><title >Courses with A-F grading scales</title ></head > <body > <form action="/Examiner" method="post"> <h2><span lang="en">Course code graded A-F</span>|<span lang="sv">Kurskod - Betygsatt exjobb (A-F)</span></h2> <select if="selected_course" name="selected_course"> #{@course_options} </select> <br><input type='submit' value='Submit' /> </form> </body > </html > HTML
The ‘/grading_scale_PF’ route, shown in Listing 4-47, uses the exact same
mechanism to render an HTML form but displays only courses that use the
Pass/Fail grading system and are suitable for the user’s program of study, with the
only major differences being the following:
1. In the first section of the route, all the degree projects with the Pass/Fail grading
system are sorted.
2. The literal ‘PF’ is passed as an argument when making a call to the
‘filter_courses_for_a_program’ function.
As in the ‘/grading_scale_AF’ route, an HTML form is rendered which
displays a drop-down list. However, in this case, the drop-down list contains only
those courses that are suitable for the user’s program of study and cycle number
and use the Pass/Fail grading scale. Furthermore, as in the ‘/grading_scale_AF’
route, when the user selects a course and submits the form, he or she is directed to
the ‘/Examiner’ route.
Implementation | 83
Listing 4-47: The ‘/grading_scale_PF’ route
get '/grading_scale_PF' do puts("in the handler for grading_scale_PF") @courses=PF_courses.sort @program_code=session['program_code'] @courses = filter_courses_for_a_program( @program_code, cycle_number, 'PF', @courses) @course_options='' @courses.each do |course| @title=relevant_courses_English[course]['title'] @title_s=relevant_courses_Swedish[course]['title'] @credits=relevant_courses_English[course]['credits'] @course_options=@course_options+'<option value="'+course+'">'+course+': '+@credits+' '+@title+' | '+@title_s+'</option>' end <<-HTML <html > <head ><title >Courses with P/F grading scales</title ></head > <body > <form action="/Examiner" method="post"> <h2><span lang="en">Course code with Pass/Fail grading</span>|<span lang="sv">Kurskod med betygsatt Godkänd eller underkänd</span></h2> <select if="selected_course" name="selected_course"> #{@course_options} </select> <br><input type='submit' value='Submit' /> </form> </body > </html > HTML end
84 | Implementation
4.4.8 Selecting an Examiner
The ‘/Examiner’ route is the route responsible for presenting a list of examiner
options to the user, based on the user’s selected degree project course. The list of all
examiners is retrieved as shown in Listing 4-48. This code runs when the Ruby
program starts.
Listing 4-48: Retrieving a list of all examiners
all_course_examiners=all_data['all_course_examiners']
It is worth noting that each value of the 'all_course_examiners' object is of the
format shown in Listing 4-49. Each value in the object is a key-value pair, with the
key being the degree project course code and the value being an array of examiners
for that course.
Listing 4-49: Format of each value in the 'all_course_examiners' object
"DA235X": ["Arvind Kumar", "Atsuto Maki", "Bob Sturm", "Christian Smith", "Christopher Peters", "Cyrille Artho", "Danica Kragic Jensfelt", "Dilian Gurov", "Elena Troubitsyna", "Erik Frans\u00e9n", "Erwin Laure", "Florian Pokorny", "Gabriel Skantze", "Hedvig Kjellstr\u00f6m", "Joakim Gustafsson", "Johan Hoffman", "Johan H\u00e5stad", "John Folkesson", "Jonas Beskow", "Jussi Karlgren", "Mads Dam", "Mario Romero Vega", "Martin Monperrus", "Mathias Ekstedt", "M\u00e5rten Bj\u00f6rkman", "Olof B\u00e4lter", "Olov Engwall", "Patric Jensfelt", "Pawel Herman", "Philipp Haller", "Robert Lagerstr\u00f6m", "Sonja Buchegger", "Stefano Markidis", "Sten Ternstr\u00f6m", "Tino Weinkauf", "Viggo Kann", "\u00d6rjan Ekeberg"]
The ‘/Examiner’ route contains two sections of code. In the first section, shown in Listing 4-50, the route checks if the key ‘selected course’ exists in the params hash, and if so, it retrieves the degree project course that was selected by the user with params[‘selected_course’] and stores it into the user’s session under the key ‘selected_course’.
Listing 4-50: First section the of ‘/Examiner’ route
if params.has_key?('selected_course') @selected_course = params['selected_course'] puts "selected_course is #{@selected_course}" session['selected_course']=@selected_course # store it in the session for use later end
In the second section of the ‘/Examiner’ route, shown in Listing 4-51, the list of
all the examiners for the selected degree project course are sorted and stored into
the variable ‘@potential_examiners’. If the length of ‘@potential_examiners’ is
greater than 0 (i.e. there is at least 1 examiner for the user’s selected course), then
the route turns each examiner in the list into a value attribute for HTML’s options
tag and renders an HTML form that prompts the user to select an examiner from a
drop-down list (where each value of the drop-down list is an examiner stored in
‘@potential_examiners’). If the user selects an examiner and submits the form,
then the route redirects to the ‘/Outcome’ route. However, if the length of
‘@potential_examiners’ was 0 (i.e., there was no examiner for the user’s selected
Implementation | 85
course), the route instead renders an HTML form that simply displays a message to
notify the user that there are no examiners available for the user’s selected course
and redirects to the ‘/OutcomeNoExaminer’ route.
Listing 4-51: Second section of the ‘/Examiner’ route
@potential_examiners=all_course_examiners[@selected_course].sort puts("@potential_examiners is #{@potential_examiners}") if @potential_examiners.length > 0 @examiner_options='' @potential_examiners.each do |examiner| #puts("examiner is #{examiner}") @examiner_options=@examiner_options+'<option value="'+examiner+'">'+examiner+'</option>' end <<-HTML <html > <head ><title >Potential Examiner|Potentiell Examinator</title ></head > <body > <form action="/Outcome" method="post"> <h2><span lang="en">Potential Examiner</span>/<span lang="sv">Potentiell Examinator</span></h2> <select if="selected_examiner" name="selected_examiner"> #{@examiner_options} </select> <br><input type='submit' value='Submit' /> </form> </body > </html > HTML else puts("There are no examiners for the course #{@selected_course}") <<-HTML <html > <head ><title >Examiner|Examinator</title ></head > <body > <h2><span lang="en">Examiner</span>/<span lang="sv">Examinator</span></h2> <p><span lang="en">There are no examiners for the course #{@selected_course}.</span> | <span lang="sv">Det finns ingen examinator för kursen #{@selected_course}.</span></p> </body > </html > HTML redirect to("/OutcomeNoExaminer") end
86 | Implementation
The ‘/OutcomeNoExaminer’ route, shown in Listing 4-52, retrieves the course
selected by the user from the user’s session (if the session has the corresponding
key) and renders an HTML form that tells the user that he or she will need to speak
with the education office in order to find an examiner.
Listing 4-52: The ‘/OutcomeNoExaminer’ route
get '/OutcomeNoExaminer' do if session.has_key?('selected_course') @selected_course = session['selected_course'] puts "selected_course is #{@selected_course}" end <<-HTML <html > <head ><title ><span lang="en">Outcome without examiner</span> | <span lang="sv">Utfall utan examinator</span></title ></head > <body > <p><span lang="en">Thank you for selecting course code #{@selected_course}. Please speak with the education office to find an examiner.</span> | <span lang="sv">Tack för att du valt kurskod #{@selected_course}. Snälla tala med utbildningskontoret för att hitta en examinator.</span<p> <p><span lang="en">You have finished the replacement for the paper form. Best of success in your degree project.</span> | <span lang="sv">Du har slutfört ersättningen för pappersblanket. Bäst av framgång i ditt examensarbete.</span></p> </body > </html > HTML end
4.4.9 Filling in the cells of the gradebook
The ‘/Outcome’ route executes the final stages of the LTI tool. It is primarily
responsible for taking the information that was input by the user during the quiz
and placing this information into appropriate cells of a Canvas course gradebook,
an action which relies on the Canvas API. However, before the ‘/Outcome’ route is
explained, it is worth explaining some of the requests used for accessing and
manipulating custom columns in a Canvas course, as well as the helper-functions
that were created (which the ‘/Outcome’ route depends on).
Each custom column in a gradebook in a Canvas course is in the form of an
object called a ‘CustomColumn’ [49]. This CustomColumn is described in Listing
4-53.
Implementation | 87
Listing 4-53: Format of the ‘CustomColumn’ object, data taken from [49]
{ "id": 2, // The ID of the custom gradebook column "teacher_notes": false, // When true, this column's visibility will be // toggled in the Gradebook when a user selects to // show or hide notes "title": "Stuff", // header text "position": 1, // column order "hidden": false, // if hidden is true , column will not be displayed "read_only": true // uneditable in the gradebook UI }
As described in [49], the ‘CustomColumn’ object has several keys, including the
ID of the column, the title of the column, and the access rights to the column. Due
to this structure, the column settings can be easily manipulated by referencing the
appropriate key. For example, the ID of a custom column called ‘col’, can be
retrieved as ‘col[‘id’]’. Likewise, the title of the column can be retrieved by using
‘col[‘title’]’.
The contents of a CustomColumn object is in the form of another object called
‘ColumnDatum’ with a key to access the contents of the column and a key
containing a Canvas user_id [49]. The format of this column is shown in Listing
4-54.
Listing 4-54: The format of the ‘ColumnDatum’ object
{ "content": "Lactose intolerance", "user_id": 4 }
The gradebook columns can be manipulated by making various HTTP requests
via the Canvas API. Table 4-4 shows some of these requests and their functions
[49].
Table 4-4: Some of the requests for Canvas’s custom gradebook columns API
HTTP request and request path Description GET /api/v1/courses/:course_id/custom_gradebook_columns Retrieves a list of all
custom gradebook columns for a course
POST /api/v1/courses/:course_id/custom_gradebook_columns Creates a custom gradebook column
PUT /api/v1/courses/:course_id/custom_gradebook_columns/:id/data/:user_id
Sets the data to be stored by a custom gradebook column
88 | Implementation
The first helper-function is the ‘list_custom_columns’ function, shown in
Listing 4-54. This function takes the variable ‘course_id’ (which represents the
Canvas ID of a course) as the only parameter, then makes a GET request using
‘/api/v1/courses/#{course_id}/custom_gradebook_columns’ as the path and
returns the response of the GET request, which is a potentially paginated list of all
the custom gradebook columns that were created for the course with the course ID
specified by ‘course_id’.
Listing 4-55: ‘list_custom_columns’ function (showing the essential code)
def list_custom_columns(course_id) @url = "http://#{$canvas_host}/api/v1/courses/#{course_id}/custom_gradebook_columns" @getResponse = HTTParty.get(@url, :headers => $header ) return @getResponse end
The second helper-function is the ‘lookup_column_number’ function, shown in
Listing 4-56. Given a column title and the list of all custom columns, this function
iterates through the list of the custom columns until it finds the column whose title
matches the column title in the parameter and simply returns the ID of the column.
Listing 4-56: ‘lookup_column_number' function (showing the essential code)
def lookup_column_number(column_name, list_of_exiting_columns) list_of_exiting_columns.each do |col| if col['title'] == column_name return col['id'] end end return -1 end
The last helper-function is the ‘put_custom_column_entries_by_name’
function, shown in Listing 4-57. This function takes 5 parameters: ‘course_id’,
‘column_name’ (which represents a column title), ‘user_id’ (which contains the
‘custom_canvas_user_id’ for the user and is stored in the user’s session),
‘data_to_store’ (which represents the content to be stored in a custom column),
‘list_of_existing_columns’ (which represents the list of all custom columns for
the course with ‘course_id’). The function first retrieves the ID of a custom
column by using ‘column_name’ and ‘list_of_existing_columns’ as arguments
and stores it in the variable ‘column_number’. It then initializes the datum to be
stored in the column as a variable called ‘payload’ and updates the column (with
the column ID specified by ‘column_number’) by sending a PUT request using the
path ‘/api/v1/courses/#{course_id}/custom_gradebook_columns/#{@column_number}/data/#{user_id}’ and
by using the value of the variable ‘payload’ as the body of the request. Finally, the
function returns the response of the PUT request.
Implementation | 89
Listing 4-57: ‘put_custom_column_entries_by_name’ function (showing only the
essential code)
def put_custom_column_entries_by_name(course_id, column_name, user_id, data_to_store, list_of_exiting_columns) @column_number=lookup_column_number(column_name, list_of_exiting_columns) @url = "http://#{$canvas_host}/api/v1/courses/#{course_id}/custom_gradebook_columns/#{@column_number}/data/#{user_id}" @payload={'column_data': {'content': data_to_store}} @putResponse = HTTParty.put(@url, :body => @payload.to_json, :headers => $header ) return @putResponse end
The ‘/Outcome’ route can be broken up into three sections of code. In the first
section, shown in Listing 4-58, the route checks if the user’s session contains the
key ‘selected_course’, and then if the session contains this key, it retrieves the
corresponding value of the key (which is the course code of the degree project
course that was selected by the user). If so, then the route stores this value into the
user’s session under the key ‘selected_course’. Next, the route checks if there
exists a value corresponding to the key ‘selected_examiner’ in the params hash.
If so, then the route retrieves the value (which is the examiner that was selected by
the user) and stores it into the user’s session under the key ‘selected_examiner’.
However, if the value does not exist, the route simply saves the string “No examiner
selected” into the variable ‘@selected_examiner’.
Listing 4-58: First section of the ‘/Outcome’ route (edited to show only the essential code)
if session.has_key?('selected_course') @selected_course = session['selected_course'] end if params.has_key?('selected_examiner') @selected_examiner = params['selected_examiner'] # mark the examainer as tentative
session['selected_examiner']='⚠⚠'+@selected_examiner else @selected_examiner = "No examiner selected" end
The second section of the ‘/Outcome’ route, shown in Listing 4-59, first saves a
list of the custom gradebook columns into a variable. This is done by making a call
to the ‘list_custom_columns’ helper function. The ID of the custom Canvas
course, corresponding to the value of the key ‘custom_canvas_course_id’ in the
user’s session, is used as the argument when making this function call. The return
value from the function call is the list of all custom gradebook columns and the
value is stored in the variable ‘@list_of_existing_columns’. Next, it retrieves the
column ID of the column titled ‘Examiner’ (by using the ‘lookup_column_number’
helper function) and saves it in ‘@col_number’.
90 | Implementation
Listing 4-59: The second section of the ‘/Outcome’ route
@list_of_existing_columns=list_custom_columns(session['custom_canvas_course_id']) @col_number=lookup_column_number('Examiner', @list_of_existing_columns)
The third section of the ‘/Outcome’ route, although relatively straightforward, is
the most complex section of the route. This section of the route, shown in Listing
4-60, makes repeated calls to the ‘put_custom_column_entries_by_name()’
function, with each call retrieving a different value from the user’s session and
storing it as the content of the appropriate column. For instance, the first call
retrieves the examiner that was selected by the user (from the user’s session) and
stores this value as the content of the column entitled Examiner, the second call
retrieves the course code for the degree project course that was selected by the user
and stores it as the content of the column entitled Course_code, and so on. It is
important to note that certain checks are made before some of the calls to ensure
that the key corresponding to the value to be retrieved exists in the user’s session
and that the value itself exists.
Listing 4-60: Third section of the ‘/Outcome’ route (edited to show only the essential
code)
result=put_custom_column_entries_by_name(session['custom_canvas_course_id'], 'Examiner', session['custom_canvas_user_id'], session['selected_examiner'], @list_of_exiting_columns) result=put_custom_column_entries_by_name(session['custom_canvas_course_id'], 'Course_code', session['custom_canvas_user_id'], session['selected_course'], @list_of_exiting_columns) if session.has_key?('diva_permission') and session['diva_permission'].length > 0 result=put_custom_column_entries_by_name(session['custom_canvas_course_id'], 'Student_approves_fulltext', session['custom_canvas_user_id'], session['diva_permission'], @list_of_exiting_columns) end if session.has_key?('Tentative_title') and session['Tentative_title'].length > 0 result=put_custom_column_entries_by_name(session['custom_canvas_course_id'], 'Tentative_title', session['custom_canvas_user_id'], session['Tentative_title'], @list_of_exiting_columns) end if session.has_key?('prelim_description') and session['prelim_description'].length > 0 result=put_custom_column_entries_by_name(session['custom_canvas_course_id'], 'Prelim_description', session['custom_canvas_user_id'], session['prelim_description'],
Implementation | 91
@list_of_exiting_columns) end place={} if session.has_key?('company') and session['company'].length > 0 place['company']=session['company'] end if session.has_key?('university') and session['university'].length > 0 place['university']=session['university'] end if session.has_key?('country_code') and session['country_code'].length > 0 place['country_code']=session['country_code'] end if place.length > 0 place_as_string=place.collect { |k,v| "#{k} = #{v}" }.join(", ") result=put_custom_column_entries_by_name(session['custom_canvas_course_id'], 'Place', session['custom_canvas_user_id'], place_as_string, @list_of_exiting_columns) end if session.has_key?('contact') and session['contact'].length > 0 result=put_custom_column_entries_by_name(session['custom_canvas_course_id'], 'Contact', session['custom_canvas_user_id'], session['contact'], @list_of_exiting_columns) end if session.has_key?('planned_start') and session['planned_start'].length > 0 result=put_custom_column_entries_by_name(session['custom_canvas_course_id'], 'Planned_start_date', session['custom_canvas_user_id'], session['planned_start'], @list_of_exiting_columns) end
As one can see, things get slightly more complicated placing information
regarding the user’s selected place where the project work is to be carried out into
the gradebook. Instead of placing the user’s selected company, country, and (other)
university into separate gradebook columns for Company, Country, and University
respectively, they are placed in a single column titled Place. This is achieved by the
following steps:
1. Creating a hash called ‘place’
2. Retrieve the user’s selected company from the session and store it into the hash
under the key ‘company’.
3. Retrieve the user’s selected university from the session and store it into the hash
under the key ‘university’.
4. Retrieving the user’s selected country from the session (where it is saved as
country code) and store it into the hash under the key ‘country_code’.
5. Use the ‘collect’ function to create an array using the contents of the hash,
where each element of the array is in the form key=value (where value
92 | Implementation
represents an element in the hash, and key represents the key corresponding to
that element).
6. Use the ‘join’ function to convert the contents of the array into a single
comma-separated string
7. Call the ‘put_custom_column_entries_by_name()’ function by using the
string as the payload and using the column entitled Place as the column whose
contents will be set to the payload
In the fourth and final section of the ‘/Outcome’ route, shown in Listing 4-61, an
HTML form is created that will later be rendered. This form simply thanks the user
for selecting a degree project course with a specific examiner and notifies him or
her that the dynamic survey has been completed. Note that the information is
presented in both English and Swedish.
Listing 4-61: Final section of the ‘/Outcome’ route
<<-HTML <html > <head ><title ><span lang="en">Outcome</span> | <span lang="sv">Utfall</span></title ></head > <body > <p><span lang="en">Thank you for selecting course code #{@selected_course} and potential examiner #{@selected_examiner}.</span> | <span lang="sv">Tack för att du valt kurskod #{@selected_course} och potentiell examinator #{@selected_examiner}.</span<p> <p><span lang="en">You have finished the replacement for the paper form. Best of success in your degree project.</span> | <span lang="sv">Du har slutfört ersättningen för pappersblanket. Bäst av framgång i ditt examensarbete.</span></p> </body > </html > HTML
4.4.10 Modification to the ‘/OutcomeNoExaminer’ route
In an updated version of the dynamic survey program, the
‘/OutcomeNoExaminer’ route was modified so that all the student information is
used to occupy the gradebook (see Listing 4-62). Additionally, unlike the
‘/Outcome’ route, this route does not check if a value for the examiner is saved in
the ‘params’ hash. Instead, it directly saves the string “No examiner selected”
into the ‘session’ hash by using ‘selected_examiner’ as the key.
Implementation | 93
Listing 4-62: Modified version of the '/OutcomeNoExaminer' route
get '/OutcomeNoExaminer' do if session.has_key?('selected_course') @selected_course = session['selected_course'] end
@selected_examiner = "No examiner selected"
@list_of_existing_columns=list_custom_columns(session['custom_canvas_course_id'])
@col_number=lookup_column_number('Examiner', @list_of_existing_columns)
result=put_custom_column_entries_by_name(session['custom_canvas_course_id'], 'Examiner', session['custom_canvas_user_id'], session['selected_examiner'], @list_of_exiting_columns) result=put_custom_column_entries_by_name(session['custom_canvas_course_id'], 'Course_code', session['custom_canvas_user_id'], session['selected_course'], @list_of_exiting_columns) if session.has_key?('diva_permission') and session['diva_permission'].length > 0 result=put_custom_column_entries_by_name(session['custom_canvas_course_id'], 'Student_approves_fulltext', session['custom_canvas_user_id'], session['diva_permission'], @list_of_exiting_columns) end if session.has_key?('Tentative_title') and session['Tentative_title'].length > 0 result=put_custom_column_entries_by_name(session['custom_canvas_course_id'], 'Tentative_title', session['custom_canvas_user_id'], session['Tentative_title'], @list_of_exiting_columns) end if session.has_key?('prelim_description') and session['prelim_description'].length > 0 result=put_custom_column_entries_by_name(session['custom_canvas_course_id'], 'Prelim_description', session['custom_canvas_user_id'], session['prelim_description'], @list_of_exiting_columns) end place={} if session.has_key?('company') and session['company'].length > 0 place['company']=session['company'] end if session.has_key?('university') and session['university'].length > 0 place['university']=session['university']
94 | Implementation
end if session.has_key?('country_code') and session['country_code'].length > 0 place['country_code']=session['country_code'] end if place.length > 0 place_as_string=place.collect { |k,v| "#{k} = #{v}" }.join(", ") result=put_custom_column_entries_by_name(session['custom_canvas_course_id'], 'Place', session['custom_canvas_user_id'], place_as_string, @list_of_exiting_columns) end if session.has_key?('contact') and session['contact'].length > 0 result=put_custom_column_entries_by_name(session['custom_canvas_course_id'], 'Contact', session['custom_canvas_user_id'], session['contact'], @list_of_exiting_columns) end if session.has_key?('planned_start') and session['planned_start'].length > 0 result=put_custom_column_entries_by_name(session['custom_canvas_course_id'], 'Planned_start_date', session['custom_canvas_user_id'], session['planned_start'], @list_of_exiting_columns)
end
<<-HTML
<html >
<head ><title ><span lang="en">Outcome without examiner</span> | <span
lang="sv">Utfall utan examinator</span></title ></head >
<body >
<p><span lang="en">Thank you for selecting course code
#{@selected_course}. Please speak with the education office to find an
examiner.</span> | <span lang="sv">Tack för att du valt kurskod
#{@selected_course}. Snälla tala med utbildningskontoret för att hitta en
examinator.</span<p>
<p><span lang="en">You have finished the replacement for the paper
form. Best of success in your degree project.</span> | <span lang="sv">Du har
slutfört ersättningen för pappersblanket. Bäst av framgång i ditt
examensarbete.</span></p>
</body >
</html >
HTML
end
Implementation | 95
4.5 Embedding the Dynamic Survey into Canvas
Figure 4-22 shows the set-up information required to configure an LTI-based
external tool in the Canvas LMS. This configuration must be done from the course
administrator’s account*. The information required to enter the administrator’s
account is shown in Section 4.1. The menu in the figure can be accessed via:
Settings Apps External Apps +App.
Figure 4-22: Setting up an LTI-based external tool in Canvas
Figure 4-23 shows the set-up information required to configure an assignment
to use the LTI-based external tool. This configuration must also be made from the
course administrator’s account. The menu in the figure can be accessed via:
Settings Assignments +Assignments. The name of the tool is entered in the
top-most text input field shown in the figure, labeled as ‘Input A’. One can add
some additional information in the input field labeled ‘Input B’. Since the
application is just a survey intended to collect information, it is not necessary to
score the user’s responses; hence, the ‘Points’ field is set to 0. Furthermore, the
‘Assignment Group’ is set to ‘Assignments’ and the final grade of the survey is set to
be displayed as points. Once again, since this program is just a survey, there is no
need to count this assignment towards a final grade. Finally, the submission type
must be set as ‘External Tool’ and the URL of the external tool must be set to the
route of the program from which the program is initiated. In this case, the route * In some Canvas instances, only a system administrator can add new LTI tools.
96 | Implementation
was set to ‘http://localhost:4567/start’. It should be noted that by default,
running a Ruby application that uses the Sinatra framework initiates a Sinatra web
server that runs on the localhost and listens on port 4567.
Figure 4-23: Setting up an assignment to use the LTI-based external tool
Analysis | 97
5 Analysis
After successfully implementing the dynamic survey system, the entire system was
tested from a user’s perspective.
In the program for the dynamic survey system, the ‘with_contraints’ toggle
was set to ‘true’ so that the ‘course-data-EECS-cycle-2c.json’ could be used as
the reference file. In this reference file, the cycle number is set to 2.
In order for the integration tests to be successful, the dynamic survey system
was required to meet the requirements shown in Table 3-2 in Section 3.1.4.
For testing, the program with all the code for the dynamic survey (delineated in
Section 4.4) is run with the command ‘ruby SinatraTest19.rb’, where
SinatraTest19 is the name of the file (without the modification in Section 4.4.10).
This made the survey available on port 4567 of the localhost.
Section 5.1 shows the resulting user interface of the system after the program is
embedded in Canvas as an external tool. Section 5.2 discusses some of the aspects
of the system in terms of reliability and validity.
5.1 Results from the Integration Tests
As mentioned in Section 3.3, the dynamic survey was tested from a user’s
perspective to ensure that every implemented function worked correctly. When
testing with a fake student who already has program data available in the system, it
was seen that the system correctly retrieved the user’s program of study. This is
shown in Figure 5-1 where the quiz starts by directly prompting the user for general
information, rather than asking about the user’s program of study.
Figure 5-1: First page of the dynamic survey when the user has program data available
98 | Analysis
When testing the system with no program data available, the system detected
this and prompts the user to enter the study program (as shown in Figure 5-2) as
the first piece of information required from the user.
Figure 5-2: First page of the survey when the user does not have program data
available
With the results shown in Figure 5-1 and Figure 5-2, it was concluded that the
first requirement from Table 3-2 has been met.
It is worth noting that this test repeatedly failed during the initial phases of the
integration tests. The result was a drop-down menu that contained every single
study program (including 1st-cycle study programs) from the reference file, which
was not desired. After some in-depth analysis of the code, it was later found that
the version used for the integration test did not contain the program-filtering logic.
This issue was resolved by simply running the integration test with the correct
dynamic survey program, which contained the program-filtering logic. This
resulted in the drop-down menu shown in Figure 5-2.
One of the fake users tested was set up as being part of the Master’s Programme
in ICT Innovation (TIVNM). When analyzing the general data questions page for
this user, it was observed that the system prevented the user from selecting a
grading scale. Given that the code for the dynamic quiz was written such that
TIVNM students must use the A-F grading scale, this behavior was as expected.
This is shown in the area marked in red in Figure 5-3.
Analysis | 99
Figure 5-3: Notifying TIVNM students that they must have the A-F grading scale
On the other hand, if the user was not part of the TIVNM program, the quiz
would have shown the option for selecting the grading scale, as shown in the
bottom portion of Figure 5-4.
Figure 5-4: Allowing non-TIVNM students to select a grading scale
With the results shown in Figure 5-3 and Figure 5-4, it was concluded that the
second requirement from Table 3-2 has been met.
The dynamic survey also successfully managed to filter the list of degree project
courses and prompt the user to select only a degree project that can be taken for
his/her study program and selected choice of grading scale. For instance, Figure
5-5 shows the list of all the degree project courses with the P/F grading scale that
the user can select if the program of study is the Degree Programme in Electrical
Engineering (CELTE), Figure 5-6 shows the list of all degree project courses with
the P/F grading scale that the user can select if the program of study is Master’s
100 | Analysis
Programme in Communication Systems (TCOMM), and Figure 5-7 shows the list of
all degree project courses with the A-F grading scale that the user can select if the
program of study is Master’s Programme in Nanotechnology (TNTEM).
Figure 5-5: Pass/Fail courses selectable for CELTE students
Figure 5-6: Pass/Fail courses selectable by TCOMM students
Figure 5-7: A-F courses selectable by TNTEM students
The selectable course options in the drop-down menu were extracted from the
course options specified in the ‘course-data-EECS-cycle-2c.json’ reference file
for each tested study program. The program codes, as well as their corresponding
course codes, are shown in Listing 5-1 (for CELTE),
Listing 5-2 (for TCOMM), and Listing 5-3 (for TNTEM).
Listing 5-1: Course codes for CELTE for cycle 2 and P/F grading scale in the reference
file
"CELTE": ["II245X", "IL248X"]
Listing 5-2: Course codes for TCOMM for cycle 2 and P/F grading scale in the reference
file
"TCOMM": ["IL246X"]
Analysis | 101
Listing 5-3: Course codes for TNTEM for cycle 2 and A-F grading scale in the reference
file
"TNTEM": ["IL226X", "IF226X"]
As one can see, the course codes in Listing 5-1 (for CELTE),
Listing 5-2 (for TCOMM), and Listing 5-3 (for TNTEM) are congruent with the
course codes shown in Figure 5-5, Figure 5-6, and Figure 5-7 respectively.
With the results shown in Figure 5-5, Figure 5-6 and Figure 5-7, it was
concluded that the third requirement from Table 3-2 has been met.
Another test that the dynamic survey successfully passed is filtering the list of
examiners and prompting the user to select only an examiner available for his/her
choice of degree project course. For instance, the left part of Figure 5-8 shows all
the examiners that the user can select if the degree project course chosen is
DA224X, while the right part of Figure 5-8 shows all the examiners that the user
can select if the degree project course chosen is IL246X.
Figure 5-8: Presenting examiner options based on selected degree project course (left
shows examiners for the course DA224X, while the right shows examiners
for the course IL246X)
When compared with the corresponding examiners list in the reference file, it
was seen that the examiner lists shown in Figure 5-8 are congruent with the course-
examiner pairings in the reference file.
With the results shown in Figure 5-8, it was concluded that the fourth
requirement from Table 3-2 has been met.
The dynamic survey also displayed the correct text on the final page based on
the examiner data. For instance, Figure 5-9 shows the final page of the dynamic
102 | Analysis
survey if the user has selected DA224X as the project course and Anders Lansner as
the examiner, while the bottom part of Figure 5-10 shows the final page of the
dynamic survey if the user has selected IL226X as the project course, which would
lead to no examiner being found by the system. The test in Figure 5-10 was run
after temporarily modifying the reference file so that IL226X doesn’t map to any
examiners.
Figure 5-9: Final page of the dynamic survey for course: DA224X and examiner: Anders
Lansner
Figure 5-10: Final page of the dynamic survey for a course with no examiner
With the results shown in Figure 5-9 and Figure 5-10, it was concluded that the
fifth requirement from Table 3-2 has been met.
Finally, it was also observed that the system successfully filled the correct cells
of the gradebook by using the information provided by the user in the dynamic
quiz. The gradebook can be accessed by entering the test course from the course
administrator’s account. The results are shown in Figure 5-11 (for students Bertil,
Ellen and Quentin).
Figure 5-11: The gradebook after server students have answered the dynamic survey
With the results shown in Figure 5-11, it was concluded that the sixth
requirement from Table 3-2 has been met.
The gradebook logic was also tested with a version of the dynamic survey
program with the modified version of the ‘/OutcomeNoExaminer’ route shown in
Listing 4-62 (in section 4.4.10). The gradebook successfully managed to occupy the
cells of the gradebook even when no examiner was available for the selected course.
Analysis | 103
The gradebook after being tested with the modified file (which was named as
SinatraTest20.rb) is shown in Figure 5-12. The test was run from Quentin
FakeStudent’s account after temporarily modifying the reference file so that
IL226X doesn’t map to any examiners.
Figure 5-12: The gradebook tested with SinatraTest20
5.2 Efficiency Evaluation
In order to evaluate the dynamic survey’s efficiency, two sample routes were
created to collect various numerical data from the ‘course-data-EECS-cycle-2c.json’ reference file. In order to verify the sample routes, they were first tested
with a sample reference file written in JSON, which follows a very similar structure
to the ‘course-data-EECS-cycle-2c.json’ reference file. After both sample
routes were successfully verified, testing for efficiency analysis was initiated with
the ‘course-data-EECS-cycle-2c.json’ reference file. The first sample route,
called ‘CourseProgramStatistics’ is shown in Listing 5-4.
Listing 5-4: Sample route created to collect numerical data regarding courses and
programs
get '/CourseProgramStatistics' do cycle_code='cycle'+"2" allCoursesList=[] minCourses = 1000; maxCourses = 0 AF_courses.each do |course| unless allCoursesList.include?(course) allCoursesList << course end end PF_courses.each do |course| unless allCoursesList.include?(course) allCoursesList << course end end allPrograms = programs_in_the_school_with_titles.sort
104 | Analysis
programs = $programs_in_the_school_with_titles.sort programs.each do |study_program| program_code = study_program[0] #puts("Program name is #{program_code}") available_AF_courses = $AF_course_codes_by_program[cycle_code][program_code] if not available_AF_courses #puts("") else if available_AF_courses.length > maxCourses maxCourses = available_AF_courses.length end if available_AF_courses.length < minCourses minCourses = available_AF_courses.length end end available_PF_courses = $PF_course_codes_by_program[cycle_code][program_code] if not available_PF_courses #puts("") else if available_PF_courses.length > maxCourses maxCourses = available_AF_courses.length end if available_PF_courses.length < minCourses minCourses = available_PF_courses.length end end end sorted_courses = allCoursesList.sort numCourses = sorted_courses.length puts("Total no. of programs is #{allPrograms.length}") puts("No. of cycle-2 programs is #{programs.length}") puts("Total number of courses is #{numCourses}") puts("Max number of courses is #{maxCourses}") puts("Min number of courses is #{minCourses}") end The sample route uses some variables that are already outlined in Section 4.4. It
uses information retrieved from the reference file to show the following
information:
1. Total number of study programs
2. Number of 2nd
cycle study programs
3. Total number of degree project courses
4. Maximum number of degree project courses found for a study program
5. Minimum number of degree project courses found for a study program
Analysis | 105
Running the program produced the output shown in Listing 5-5.
Listing 5-5: Output from the 'CourseProgramStatistics' route
Total no. of programs is 22 No. of cycle-2 programs is 18 Total number of courses is 79 Max number of courses is 2 Min number of courses is 1
From the output in Listing 5-5, the following were concluded:
1. The program-filtering logic of the dynamic survey has reduced the number of
study programs selectable by the user from 22 to 18. Assuming that it would
take constant time to read the entire name of a study program, this will reduce
the time taken by the 2nd
cycle student to read through the list of all the study
programs by 18.2%.
2. Depending on the study program selected, the course-filtering logic of the
dynamic survey has reduced the number of selectable degree project course
from 79 to either 1 or 2. Assuming that it would take constant time to read the
entire name of a course, this reduces the time taken by a 2nd
cycle student to read
through the list of degree project courses by a percentage that falls within the
interval [97.5, 98.7].
The second sample route created was used to determine the efficiency of the
dynamic survey in terms of examiner selection. The code for this second route is
shown in Listing 5-6. The code retrieves the following information that was stored
in the reference file:
1. The total number of examiners
2. The maximum number of examiners found for a course
3. The minimum number of examiners found for a course
This route produced the output shown in Listing 5-7.
Listing 5-6: Sample route created to collect numerical data regarding the examiners
get '/getExaminerStatistics' do examinerList=[] max_numExaminers = 0 max_examiner_course = "" min_numExaminers = 1000 min_examiner_course = "" AF_courses.each do |course| potential_examiners = all_course_examiners[course].sort if potential_examiners.length > max_numExaminers max_numExaminers = potential_examiners.length max_examiner_course = course end
106 | Analysis
if potential_examiners.length < min_numExaminers && potential_examiners.length != 0 min_numExaminers = potential_examiners.length min_examiner_course = course end #puts("Number of examiners for #{course} is #{potential_examiners.length}") potential_examiners.each do |current_examiner| unless examinerList.include?(current_examiner) examinerList << current_examiner end end end PF_courses.each do |course| potential_examiners = all_course_examiners[course].sort if potential_examiners.length > max_numExaminers max_numExaminers = potential_examiners.length max_examiner_course = course end if potential_examiners.length < min_numExaminers && potential_examiners.length != 0 min_numExaminers = potential_examiners.length min_examiner_course = course end #puts("Number of examiners for #{course} is #{potential_examiners.length}") potential_examiners.each do |current_examiner| unless examinerList.include?(current_examiner) examinerList << current_examiner end end end sorted_examiners = examinerList.sort puts(sorted_examiners) puts("Total number of examiners is #{sorted_examiners.length}") puts("Maximum number of examiners is #{max_numExaminers}") puts("The course with the max number of examiners is #{max_examiner_course}") puts("Minimum number of examiners is #{min_numExaminers}") puts("The course with the min number of examiners is #{min_examiner_course}") end
Listing 5-7: Output from the 'getExaminerStatistics' route
Total number of examiners is 162 Maximum number of examiners is 50 The course with the max number of examiners is DA221X Minimum number of examiners is 1 The course with the min number of examiners is ED222X
Analysis | 107
Based on the output, it was concluded that depending on the degree project
course selected, the examiner-filtering logic of the dynamic survey has reduced the
number of examiners selectable by a user from 162 to a maximum of 50 and a
minimum of 1 (with the assumption that the selected degree project has at least 1
examiner available). Assuming that it would take constant time to read the name of
an examiner, this reduces the time taken by a 2nd-cycle student to read through the
list of examiners by a percentage that falls within the interval [69.1, 99.4].
Finally, it can also be concluded that any question that is skipped by the
dynamic survey reduces the time taken by a 2nd cycle student to read that question
by 100%. Such is the case when the student has program data available in the
system and as a result, the survey does not prompt the user to select a study
program.
5.3 Validity and Reliability
Section 3.3 of this thesis thoroughly describes the variation of the Test-driven
Development (TDD) process followed to during the implementation of the
submodules. Using this development process allowed the team to focus on the main
goal of every submodule, which helped ensure that every submodule does exactly
what it needs to do without any unnecessary functions. Furthermore, the initial
stage of testing each submodule allowed for frequent sanity checks throughout the
post-implementation process. This ensured that every new submodule to be
imported will be tested in a stable and functional environment and greatly
decreased the possibilities of anomalous behavior. The testing stages of each
submodule itself included a wide variety of test cases against different parameters,
which added an additional layer of reliability to the tests. Overall, all these aspects
of the altered TDD process allowed for very thorough glass-box testing of every
submodule of the system which not only allowed for effective verification of the
system but also enhanced the overall reliability of the entire system.
In addition to the frequent tests run during the post-implementation phase, the
final system was run through integration tests against various parameters (such as
availability of study program information and selected course) to ensure that none
of the implemented functions showed any anomalous behavior. The positive results
of the altered TDD phase ensured that the results from the integration tests run
successfully. The only time the tests failed is when they were run with the incorrect
version of the dynamic survey program. The results from these tests were
illustrated in Section 5.1.
Examination of Figure 4-21 (which was derived based on the code) revealed a
design error in the code as the collect information was not being stored for a
student who selected a degree project course for which there was no examiner. This
lead to a new version of the program (SinatraTest20.rb) to store the collected
information even in this case.
Conclusions and Future work | 109
6 Conclusions and Future work
This chapter marks the conclusion of the thesis. It revisits the goals of the project,
states some of the insights gained over the course of the project, describes the
limitations faced during the project, and includes some remarks on the possibilities
for future work and how the results of the project could be enhanced.
6.1 Conclusions
During the initial stages of the project, the main goal was the creation of a quiz
system that dynamically constructs questions based on factors such as a student’s
program of study and their answers to previous questions in the quiz, with the aim
of reducing the workload on the students participating in the quiz. As shown in
Chapter 4, many of the mechanisms implemented in the dynamic quiz, such as
using the Canvas API to retrieve information regarding a student’s study program,
as well as assigning special identifiers to a student’s answer choices and saving
them in a hash for future use, have allowed the final system to be dynamic in terms
of both the questions and the answer choices presented to the student. As a result,
the system avoids students seeing both questions and answer choices which would
be irrelevant to them and effectively reduces the time it would take to complete the
survey.
The project also aimed to reduce the workload on administrators by entering
students’ answers into the appropriate cells of a gradebook. Given the large number
of students who are likely to participate in the dynamic quiz, this task avoids a
painstaking and time-consuming task for the administrators. Moreover, using the
Canvas API to automatically perform this task (as outlined in Chapter 4) offers an
effective solution to the problem.
From a broader perspective, mechanisms similar to those implemented in the
dynamic quiz, primarily the RESTful API, can be used to create a wide variety of
dynamic quizzes/forms/surveys, beyond those used for allocating students to
appropriate project courses and assigning project examiners. Use of the dynamic
survey mechanism explored in this thesis opens up a wide spectrum of possibilities
for additional automatic forms each of which can reduce the workload on students,
faculty, and administrators.
The creation of several microservices in the form of Docker containers and
allowing these containers to communicate over a single network illustrates the
possibilities for virtualizing and experimenting with software (as was done with the
Canvas LMS during this project). Thanks to the many advantages Docker has over
virtual machines, software virtualized through Docker containers is likely to run
fast and allow smoother experimentation for developers. Therefore, this approach
is recommended for others who want to develop LTI tools for use from Canvas or
any other LTI compliant TC. Applications, such as the one outlined in this thesis,
can even be containerized with Docker so that they can be invoked by other
Canvas-related microservices. This can have many advantages. For instance,
instead of installing Ruby and all its dependencies, developers could simply use a
110 | Conclusions and Future work
Ruby Docker image [50]. It can also add the benefit of parity to the application, as
Docker images will run the same on all machines, which will reduce the need for
setting up environments and debugging issues that are environment-specific [15].
On the other hand, containerizing an application can increase the performance
overhead due to the interface between the containers and consumption of
resources [51].
This project introduced the team to the concept of test-driven development
(TDD). Specifically, the TDD-variant followed in this project allowed the testing of
modules of the dynamic survey system.
According to further post-project research, it was found that another
development process that could be feasible for such a project is behavior-driven
development (BDD), primarily due to it adopting many principles of TDD [52].
6.2 Limitations
The system developed over the course of this project presents some limitations for
potential users. For instance, it does not take into account that students also need
to have a supervisor for their degree project*. However, the same concepts used for
selecting an appropriate examiner could potentially be used to develop a program
that could be used by the examiner to assign an appropriate adviser.
A second limitation is that the system only utilized program data of fake users
which were created for testing purposes. The absence of this information in the
production instance of Canvas is a major limitation. Retrieving program
information for actual KTH students is expected to either require an application
protocol known as Lightweight Directory Access Protocol (LDAP)† or an interface
to LADOK‡.
6.3 Future work
A potential area of future development is the creation of an interface between the
Canvas LMS and Ladok. This interface could be used to retrieve program and
specialization information for any student.
Once an interface to LADOK is available, then a system could be created in
which could determine whether a student who has self-enrolled in a course is
eligible to be a student in that course. This system would check whether a student
who is interested in starting a degree project has met all of the requirements for
starting their degree project course. Such a system would greatly reduce the
administrative effort in determining whether a student is qualified to start a degree
project. This would be possible because LADOK contains all the students’ grades
for each of their courses and also include information about which program of
study and specialization a student is enrolled in.
* Formally, it is the examiner who appoints the supervisor.
† LDAP is typically used for accessing various resources over an internet protocol (IP) network [53].
‡ This interface is currently under development by KTH’s IT unit.
Conclusions and Future work | 111
Such an interface could also be used by KTH to synchronize the grades between
students as recorded in Canvas and those reported in LADOK. Likewise, the system
can increase the overall efficiency of grade reporting at KTH by automatically
transferring students’ grades to Ladok. Given the tremendous amount of time
needed to report students’ final grades, such a system would greatly improve the
university’s operations.
Docker and the RESTful APIs, in general, opens up quite a wide spectrum of
other applications that can be created for the Canvas LMS. One example is an
application that tests students on their knowledge of using version control systems.
The concept of version control is very popular among development teams since it
allows them to manage the files in their large-scale projects in various ways. One of
the most popular version control system used today is Git and it even comes with
its own REST API. This Github API can be used for creating interactive “Git
challenges” for students in order to familiarize them with the version control
system so that they can prepare for working with large projects in teams.
Other LMS’s, such as Blackboard and Moodle, also come with their own API
that can be used by developers to create applications.
It is also worth researching the possibilities provided by other web service
architectures besides REST (such as GraphQL) and their potential feasibility in
conjunction with LMS applications.
Other potential areas of research include the virtualization of LMS’s by using
Docker side-by-side with a container orchestration system such as by the Cloud
Native Computing Foundation’s Kubernetes.
6.4 Reflections
This section provides some reflections on the social and economic aspects of the
project, as well as ethical aspects.
6.4.1 Social and Economic Aspects
Usually, a student who wishes to start working on a degree project is preoccupied
with other courses. Currently, it often takes students an excessive amount of effort
to find a degree project to work on and an examiner for this project. This effort has
frequently resulted in a large waste of time which could otherwise be used for
focusing on the student’s existing courses. Therefore, students would prefer that
the degree project course and examiner selection process be as efficient as possible.
The same can be said for teachers who have a large amount of other work and do
not want to have to manually organize student information in the gradebook cells.
Hence, teachers would also prefer the process to be more efficient. The system built
over the course of this project paves a way to achieve efficiency, time savings, and
greater accuracy. Additionally, it enables all of the people involved to focus on what
they actually need to do, rather than spending effort on things that could be
avoided.
112 | Conclusions and Future work
6.4.2 Ethical Aspects
On May 25, 2018, the European Union (EU) implanted a regulation known as the
General Data Protection Regulation (GDPR). This regulation aims to provide
individuals with better control over their personal data so that these data are well-
protected [3].
The process of retrieving personal information from students was one of the
pivotal parts of the system created over the course of this project. However, the
system follows GDPR standards by retrieving only the type of information that
students would want to be disclosed in an academic platform. The system avoids
making any requests to retrieve information that students would not wish to
disclose.
Moreover, the version of the system created over the course of this project does
not retrieve any information regarding real students. As outlined in Section 4.4.2,
the system only retrieves custom data for fake users.
References | 113
References
[1] “Datorbaserade system för studiedokumentation vid högskolan — STUDOK/LADOK,” 06-Dec-1984. [Online]. Available: http://data.riksdagen.se/dokument/G80411. [Accessed: 17-Apr-2019].
[2] S. Besharat Pour and Q. Li, “Connecting Silos : Automation system for thesis processing in Canvas and DiVA,” KTH, School of Electrical Engineering and Computer Science (EECS), Communication Systems, CoS, Radio Systems Laboratory (RS Lab), TRITA-EECS-EX ; 2018 [Online]. Available: http://urn.kb.se/resolve?urn=urn:nbn:se:kth:diva-230996. [Accessed: 15-Apr-2019].
[3] “Proposal for a Regulation of the European Parliament and of the Council on the protection of individuals with regard to the processing of personal data and on the free movement of such data (General Data Protection Regulation),” Council of the European Union, Brussels, 2012/0011 (COD), Jun. 2015 [Online]. Available: http://data.consilium.europa.eu/doc/document/ST-9565-2015-INIT/en/pdf. [Accessed: 15-Apr-2019].
[4] “Introduction to Test Driven Development (TDD).” [Online]. Available: http://agiledata.org/essays/tdd.html. [Accessed: 15-Apr-2019].
[5] “White Box Testing,” Software Testing Fundamentals, 19-Dec-2010 [Online]. Available: http://softwaretestingfundamentals.com/white-box-testing/. [Accessed: 15-Apr-2019].
[6] “About Us | Canvas Learning Management System.” [Online]. Available: https://www.canvaslms.com/about-us/. [Accessed: 15-Apr-2019].
[7] R. K. Ellis, A Field Guide to Learning Management Systems. American Society for Training & Development (ASTD), 2009 [Online]. Available: http://web.csulb.edu/~arezaei/ETEC551/web/LMS_fieldguide_20091.pdf. [Accessed: 15-Apr-2019].
[8] “Canvas LMS REST API Documentation.” [Online]. Available: https://canvas.instructure.com/doc/api/index.html. [Accessed: 15-Apr-2019].
[9] “Basic Overview of How LTI works | IMS Global Learning Consortium.” [Online]. Available: https://www.imsglobal.org/basic-overview-how-lti-works. [Accessed: 15-Apr-2019].
[10] A. Kokkalis, “On-demand virtual laboratory environments for Internetworking e-learning : A first step using docker containers,” KTH, School of Electrical Engineering and Computer Science (EECS), Communication Systems, CoS, TRITA-EECS-EX ; 2018:16, 2018 [Online]. Available: http://urn.kb.se/resolve?urn=urn:nbn:se:kth:diva-222010. [Accessed: 15-Apr-2019].
[11] “Learning Tools Interoperability v1.1.1 Implementation Guide | IMS Global Learning Consortium.” [Online]. Available: http://www.imsglobal.org/specs/ltiv1p1p1/implementation-guide. [Accessed: 15-Apr-2019].
[12] “What’s the Diff: VMs vs Containers,” Backblaze Blog | Cloud Storage & Cloud Backup, 28-Jun-2018 [Online]. Available: https://www.backblaze.com/blog/vm-vs-containers/. [Accessed: 15-Apr-2019].
[13] “Docker vs VMs,” DevOps.com, 24-Nov-2014. [Online]. Available: https://devops.com/docker-vs-vms/. [Accessed: 15-Apr-2019].
[14] “About images, containers, and storage drivers,” Docker Documentation, 28-Feb-2019. [Online]. Available: https://docs.docker.com/engine/userguide/storagedriver/imagesandcontainers/. [Accessed: 15-Apr-2019].
[15] “Top 10 Benefits of Docker - DZone DevOps,” dzone.com. [Online]. Available: https://dzone.com/articles/top-10-benefits-of-using-docker. [Accessed: 15-Apr-2019].
[16] C. Hoffman, “Sandboxes Explained: How They’re Already Protecting You and How to Sandbox Any Program,” How-To Geek. [Online]. Available:
114 | References
https://www.howtogeek.com/169139/sandboxes-explained-how-theyre-already-protecting-you-and-how-to-sandbox-any-program/. [Accessed: 15-Apr-2019].
[17] Jake Wright, Learn Docker in 12 Minutes [Online]. Available: https://www.youtube.com/watch?v=YFl2mCHdv24. [Accessed: 15-Apr-2019].
[18] “Overview of Docker Compose,” Docker Documentation, 12-Apr-2019. [Online]. Available: https://docs.docker.com/compose/overview/. [Accessed: 15-Apr-2019].
[19] “Networking in Compose,” Docker Documentation, 12-Apr-2019. [Online]. Available: https://docs.docker.com/compose/networking/#links. [Accessed: 15-Apr-2019].
[20] “Compose file version 2 reference,” Docker Documentation, 12-Apr-2019. [Online]. Available: https://docs.docker.com/compose/compose-file/compose-file-v2/#links. [Accessed: 15-Apr-2019].
[21] K. C. Huang, “Don’t Repeat Yourself with Anchors, Aliases and Extensions in Docker Compose Files,” Medium, 14-Oct-2017 [Online]. Available: https://medium.com/@kinghuang/docker-compose-anchors-aliases-extensions-a1e4105d70bd. [Accessed: 15-Apr-2019].
[22] “Networking in Compose,” Docker Documentation, 12-Apr-2019. [Online]. Available: https://docs.docker.com/compose/networking/. [Accessed: 15-Apr-2019].
[23] “How to Deploy Microservices with Docker,” Linode Guides & Tutorials. [Online]. Available: https://www.linode.com/docs/applications/containers/deploying-microservices-with-docker/. [Accessed: 15-Apr-2019].
[24] C. Richardson, “Inter-Process Communication in a Microservices Architecture - DZone Microservices,” dzone.com. [Online]. Available: https://dzone.com/articles/building-microservices-inter-process-communication-2. [Accessed: 15-Apr-2019].
[25] “Asynchronous message-based communication.” [Online]. Available: https://docs.microsoft.com/en-us/dotnet/standard/microservices-architecture/architect-microservice-container-applications/asynchronous-message-based-communication. [Accessed: 15-Apr-2019].
[26] “Communication in a microservice architecture.” [Online]. Available: https://docs.microsoft.com/en-us/dotnet/standard/microservices-architecture/architect-microservice-container-applications/communication-in-microservice-architecture. [Accessed: 15-Apr-2019].
[27] “What is a Reverse Proxy Server?,” NGINX. [Online]. Available: https://www.nginx.com/resources/glossary/reverse-proxy-server/. [Accessed: 15-Apr-2019].
[28] X. G. An, “Differences Between Forward Proxy and Reverse Proxy,” LinuxBabe, 02-Jan-2016 [Online]. Available: https://www.linuxbabe.com/it-knowledge/differences-between-forward-proxy-and-reverse-proxy. [Accessed: 15-Apr-2019].
[29] J. Hans, “Host multiple websites on one VPS with Docker and Nginx,” Serverwise, 05-Jun-2017 [Online]. Available: https://blog.ssdnodes.com/blog/host-multiple-websites-docker-nginx/. [Accessed: 15-Apr-2019].
[30] D. Booth et al., “Web Services Architecture.” [Online]. Available: https://www.w3.org/TR/2004/NOTE-ws-arch-20040211/#relwwwrest. [Accessed: 15-Apr-2019].
[31] A. Monus, “SOAP vs REST vs JSON comparison [2019],” Raygun Blog. [Online]. Available: https://raygun.com/blog/soap-vs-rest-vs-json/. [Accessed: 15-Apr-2019].
[32] Z. Liew, “Understanding And Using REST APIs,” Smashing Magazine, 01:38 + +0100-100AD. [Online]. Available: https://www.smashingmagazine.com/2018/01/understanding-using-rest-api/. [Accessed: 15-Apr-2019].
[33] “Courses - Canvas LMS REST API Documentation.” [Online]. Available: https://canvas.instructure.com/doc/api/courses.html. [Accessed: 03-May-2019].
[34] G. Petrović, A. Nikolić, M. Segedinac, A. Kovačević, and Z. Konjović, “Grader: An LTI app for automatic, secure, program validation using the Docker sandbox,” in ICIST 2014 Proceedings, 2014, pp. 221–224 [Online]. Available: http://www.eventiotic.com/eventiotic/library/paper/211. [Accessed: 15-Apr-2019].
References | 115
[35] “Top-Down Design (Introduction to Statistical Computing).” [Online]. Available: http://bactra.org/weblog/950.html. [Accessed: 15-Apr-2019].
[36] “What is Test Driven Development (TDD)? Tutorial with Example.” [Online]. Available: https://www.guru99.com/test-driven-development.html. [Accessed: 15-Apr-2019].
[37] “Black Box Testing,” Software Testing Fundamentals, 19-Dec-2017 [Online]. Available: http://softwaretestingfundamentals.com/black-box-testing/. [Accessed: 15-Apr-2019].
[38] “Overcommit Accounting.” [Online]. Available: https://www.kernel.org/doc/Documentation/vm/overcommit-accounting. [Accessed: 15-Apr-2019].
[39] “3.17. The ‘Packet List’ Pane.” [Online]. Available: https://www.wireshark.org/docs/wsug_html_chunked/ChUsePacketListPaneSection.html. [Accessed: 03-May-2019].
[40] “3.18. The ‘Packet Details’ Pane.” [Online]. Available: https://www.wireshark.org/docs/wsug_html_chunked/ChUsePacketDetailsPaneSection.html. [Accessed: 03-May-2019].
[41] “How are TCP/IP and HTTP related?,” SearchNetworking. [Online]. Available: https://searchnetworking.techtarget.com/answer/How-are-TCP-IP-and-HTTP-related. [Accessed: 03-May-2019].
[42] “3.19. The ‘Packet Bytes’ Pane.” [Online]. Available: https://www.wireshark.org/docs/wsug_html_chunked/ChUsePacketBytesPaneSection.html. [Accessed: 03-May-2019].
[43] “PostgreSQL: Documentation: 9.3: Frontend/Backend Protocol.” [Online]. Available: https://www.postgresql.org/docs/9.3/protocol.html. [Accessed: 15-Apr-2019].
[44] “Postgres on the wire - A look at the PostgreSQL wire protocol,” p. 55, [Online]. Available: https://www.pgcon.org/2014/schedule/attachments/330_postgres-for-the-wire.pdf. [Accessed: 03-May-2019].
[45] Guptakumartanuj, “Capture Postgres database packets through Wireshark on local machine,” D Technologist Geek, 20-Feb-2018 [Online]. Available: https://guptakumartanuj.wordpress.com/2018/02/20/capture-postgres-database-packets-through-wireshark-on-local-machine/ [Accessed: 03-May-2019].
[46] S. Vokes, “Making Diagrams with graphviz,” Atomic Spin, 30-Jan-2013 [Online]. Available: https://spin.atomicobject.com/2013/01/30/making-diagrams-with-graphviz/. [Accessed: 15-Apr-2019].
[47] “Sinatra Basics | The Odin Project.” [Online]. Available: https://www.theodinproject.com/courses/ruby-on-rails/lessons/sinatra-basics. [Accessed: 15-Apr-2019].
[48] Auth0, “Access Tokens,” Auth0 - Pageation. [Online]. Available: https://auth0.com/docs/. [Accessed: 15-Apr-2019].
[49] “Custom Gradebook Columns - Canvas LMS REST API Documentation.” [Online]. Available: https://canvas.instructure.com/doc/api/custom_gradebook_columns.html. [Accessed: 15-Apr-2019].
[50] T. Reeder, “Why and How to Use Docker for Development,” Travis on Docker, 28-Apr-2015 [Online]. Available: https://medium.com/travis-on-docker/why-and-how-to-use-docker-for-development-a156c1de3b24 [Accessed: 03-May-2019].
[51] C. Tozzi, “Docker Downsides: Container Cons to Consider before Adopting Docker,” Channel Futures, 26-May-2017. [Online]. Available: https://www.channelfutures.com/open-source/docker-downsides-container-cons-to-consider-before-adopting-docker. [Accessed: 03-May-2019].
[52] “Introduction to TDD and BDD,” Cucumber Blog, 15-May-2017. [Online]. Available: https://cucumber.ghost.io/blog/intro-to-bdd-and-tdd/. [Accessed: 03-May-2019].
[53] “What is the Lightweight Directory Access Protocol (LDAP)? -- Definition from WhatIs.com,” SearchMobileComputing. [Online]. Available: https://searchmobilecomputing.techtarget.com/definition/LDAP. [Accessed: 15-Apr-2019].
Appendix A: UT-EXAR form without heading | 117
Appendix A: UT-EXAR form without heading
Below are the questions from the UT-EXAR form as of 2016-11-14. The form was from
https://intra.kth.se/polopoly_fs/1.577031!/UT-EXAR%20Ans%C3%B6kan%20om%20examensarbete%202016-11-14.dotx .
Ansökan om examensarbete, del 1/application for degree project, Part 1
Fylls i av studenten/To be filled in by the student
Förnamn/First name
Datum/Date
Efternamn/Surname
Personnummer/Civic registration number
E-postadress/E-mail
Program vid KTH/Programme at KTH
Planerad start för examensarbete/Degree project is planned to start
☐Jag godkänner publicering via DiVA/I accept publication via DiVA
☐Jag godkänner inte publicering via DiVA/I do not accept publication via DiVA
Signatur student/Signature student
Fylls i av skolans administration/Filled in by the school administration
Studenten uppfyller poängkrav för att antas till examensarbete/The student fulfills credit
requirements for admission to degree project
Datum/Date
Underskrift behörig administrativ personal/Signature
Namnförtydligande/Printed name
Bifoga registerutdrag för studenten./Attach transcript of records for the student.
118 | Appendix A: UT-EXAR form without heading
del 2, förutsätter att del 1 har godkänts/ part 2, Requires approved part 1
Studentens namn/Name of student
Preliminär titel/Tentative title
Fylls i av examinator/To be filled in by examiner
Kort beskrivning av projekt/Short description of project
Examinator/Examiner
Kurskod* och kursnamn/Course code* and course name
Startdatum/Planned start
*Studenter som påbörjat sina studier 2007-07-01 – 2015-06-30 kan välja graderat betyg. Ange då kurskod för graderat betyg./Students who
started their studies July 1 2007 – June 30 2015 may choose grading scale A-E. If this is the students choice, use the associated course code.
Var ska examensarbetet utföras:/The degree project will be carried out:
På KTH, ange avdelning/At KTH, unit
Handledare/Supervisor
På företag, ange vilket/At a company, indicate name
Kontaktperson/Contact person
Utomlands, ange land/Outside Sweden, indic. country
Kontaktperson/Contact person
På annan högskola/At another university
Kontaktperson/Contact person
Signatur examinator/Signature examiner
Förslaget godkänns som examensarbete*/The proposal is approved as a
degree project*
Signatur grundutbildningsansvarig (GA)/Signature Director of First and Second Cycle Education (GA)
Namnförtydligande/Printed name
*GA godkänner om examensarbetet ligger utanför programmets ämnesområde./GA approves if the degree project is outside the
programme subject area.
Appendix A: UT-EXAR form without heading | 119
Ladok/Registration
Antagen till kurs, signatur och namnförtydligande Registrerad på kurs, signatur och namnförtydligande
120 | Appendix B: Location of the dynamic survey program
Appendix B: Location of the dynamic survey program
The program in which the dynamic survey was written (SinatraTest19.rb), as well
as the test file (Tests.rb), can be found in the public Github repository:
https://github.com/dynamicsurveykth/DynamicSurvey
In the test file, the ‘puts’ function is used for printing output to the console for
debugging purposes. This will allow any tester to collect the data from the tests.
The test file contains modified routes that tests the original routes of the dynamic
survey against different strings. For instance, the ‘/testPrograms’ route tests
whether the filtered array of programs contains the correct string at certain indices of
the array. Furthermore, the ‘/testGeneralData’ route tests if the information input
by the user in the general data page matches the correct strings that are hardcoded in
the program. In order for the tests to pass, the tester would have to input the same
strings exactly as shown in the route. The user may even modify the strings in the
code to check if the results from the tests are different or not. To test both of these
routes, the user must first start the Ruby program via the command ‘ruby Tests.rb’
and go to ‘localhost:4567/testPrograms’ in a browser. The results from this
program-filtering test will be output on the console. The user can then select a
program and submit the form which will route the user to the general data page,
where the user can enter all the strings that are hardcoded in the test file if he/she
wishes for the tests to pass. Once the user submits the form, the results from the tests
will be output on the console.
The route ‘/testCourses’ can be modified to test the course options for different
study programs (by changing the value of the variables ‘program’, ‘courseToTest_a’
and ‘courseToTest_b’). The ‘/testExaminers’ can be tested with the same logic.
The routes can be tested by running the Ruby program and either going to
‘localhost:4567/testCourses’ (to test the course filtering logic) or to
‘localhost:4567/testExaminers’ (to test the examiner filtering logic). The results
from the tests will be output on the console.
The repository also includes the main reference file used for testing (course-data-EECS-cycle-2c.json), as well as the reference file used for verifying the
sample routes created in Section 5.2 (TestReferenceFile.json). In order to test
with ‘TestReferenceFile.json’, one must comment out the codes in Tests.rb that
parse the data from the main reference file and uncomment the code that parses the
data from ‘TestReferenceFile.json’. The routes can be tested by running the Ruby
program and either going to ‘localhost:4567/getExaminerStatistics’ (to get
statistical data on the examiners) or ‘localhost:4567/CourseProgramStatistics’
(to get statistical data on the programs and courses). The results from the tests will be
output on the console.
The modified version of the dynamic survey program (with the changes shown in
Section 4.4.10) can also be found in the repository under the name ‘SinatraTest20.rb’.
| 121
TRITA-EECS-EX-2019:93
www.kth.se