### Generating generic questions for calendar activity.

## Overview

One of the tasks for GSoC 2019, was adding multiple datasets to calendar activities, but along with the task I also planned on adding another inprovement to the activity. The activity initially used hardcoded questions and answesr regarding calenders. The dataset for which looked like

```
data: [
{
"navigationBarVisible" : true,
"minimumDate": "2018-01-01",
"maximumDate": "2018-12-31",
"visibleMonth": 1,
"visibleYear": 2018,
"mode": "findMonthOnly",
"questionsExplicitlyGiven": true,
"questionAnswers": [
{
"question": qsTr("Find the month starting a Thursday and having 28 days"),
"answer": {"month": [1]}
},
{
"question": qsTr("Find a month starting a Monday and having 31 days"),
"answer": {"month": [0, 9]}
},
{
"question": qsTr("Find the month between June and August"),
"answer": {"month": [6]}
},
{
"question": qsTr("Find a month starting a Saturday"),
"answer": {"month": [8, 11]}
},
{
"question": qsTr("Find a month having 30 days"),
"answer": {"month": [3, 5, 8, 10]}
}
]
}, ... and more levels so on
]
```

So I planned on creating an algorithm that could generate some template questions and answers. The activity has four type of questions depending in the mode. The available modes are

```
// findMonthOnly --> For questions based on finding month only.
// findYearMonthDay --> For questions based on finding year, month and day.
// findDayOfWeek --> For questions based on finding day of week only.
// findDay --> For questions based on finding day of a given month and year.
```

So, I the algorithm should generate different questions for each type.

## Function to generate date in Range

The first task is to create a function that would generate a random date within the given range i.e minDate and maxDate.

```
function generateRandomYearMonthDay(minimumDate, maximumDate) {
var minYear = Number(minimumDate.slice(0, 4))
var maxYear = Number(maximumDate.slice(0, 4))
var minMonth = Number(minimumDate.slice(5, 7))
var maxMonth = Number(maximumDate.slice(5, 7))
var minDate = Number(minimumDate.slice(8, 10))
var currentYear = minYear + Math.floor(Math.random() * Math.floor((maxYear - minYear + 1)))
var currentMonth = minMonth + Math.floor(Math.random() * Math.floor((maxMonth - minMonth + 1)))
var currentDate
daysInMonths[1] = (isLeapYear(currentYear)) ? 29 : 28;
currentDate = minDate + Math.floor(Math.random() * Math.floor((daysInMonths[currentMonth - 1] - minDate + 1)))
return { year: currentYear, month: currentMonth - 1, day: currentDate }
}
```

## Function to add offset

There are some questions which require the user to find a date, a given number of days ahead of the current date. So I created a function to calculate that date.

```
function addOffsetToCurrentDate(currentDate) {
var maxOffset = currentLevelConfig.questionAnswers.maxOffset
var offset = Math.floor(maxOffset / 2) + Math.floor(Math.random() * Math.floor(maxOffset))
daysInMonths[1] = (isLeapYear(currentDate.year)) ? 29 : 28;
offset += currentDate.day
var answerDate = 1;
var answerMonth = currentDate.month
var answerYear = currentDate.year
while(offset > 0) {
if(offset - daysInMonths[answerMonth] > 0) {
offset -= daysInMonths[answerMonth]
answerMonth++;
} else {
answerDate = offset;
offset = 0
}
if(answerMonth > 12) {
answerYear++;
daysInMonths[1] = (isLeapYear(answerYear)) ? 29 : 28;
answerMonth = 0;
}
}
return { year: answerYear, month: answerMonth, day: answerDate, offset: offset }
}
```

## Function to generate template questions

Now there has to be a function that would return the correct question text template as per the mode of the question. So I created a function for that.

```
function getTemplateQuestionText(mode, date) {
var questionText
if(mode == "findDayOfWeek") {
questionText = qsTr("What day of the week is on %1?").arg(getDateInLongFormat(date))
} else if(mode == "findDay") {
questionText = qsTr("Select day %1?").arg(date.day)
} else if(mode == "findMonthOnly") {
questionText = qsTr("Find month number %1").arg(date.month + 1)
} else {
if(date.offset) {
//: The second argument represents the given date in complete format(with complete month name) and the first argument represents the difference in days between given date and answer date.
questionText = qsTr("Find the date %1 days after %2").arg(date.offset).arg(getDateInLongFormat(date))
} else
//: The argument represents the answer date in complete format(with complete month name)
questionText = qsTr("Find the date %1").arg(getDateInLongFormat(date))
}
return questionText
}
```

## Function to set question and answer values

Finally I created a function that would set the correct values of the question and answer variables as per the selected mode.

```
if(!currentLevelConfig.questionsExplicitlyGiven) {
var randomDate = generateRandomYearMonthDay(currentLevelConfig.minimumDate, currentLevelConfig.maximumDate)
items.score.currentSubLevel = currentSubLevel
if(currentLevelConfig.mode == "findDayOfWeek") {
var selectedDate = new Date(randomDate.year, randomDate.month - 1, randomDate.day)
correctAnswer.dayOfWeek = Number(selectedDate.getDay())
} else if(currentLevelConfig.mode == "findYearMonthDay" && currentLevelConfig.questionAnswers.maxOffset) {
correctAnswer = addOffsetToCurrentDate(randomDate)
randomDate.offset = correctAnswer.offset
} else {
correctAnswer = randomDate
}
items.questionItem.text = getTemplateQuestionText(currentLevelConfig.mode, randomDate)
}
```

The part of code is executed only if the questions are not explicitly given.

## Dataset contents for template questions

If the dataset creater wants to use template function then he can define then like

```
{
"navigationBarVisible" : true,
"minimumDate": "2018-01-01",
"maximumDate": "2019-12-31",
"visibleMonth": 10,
"visibleYear": 2018,
"mode": "findYearMonthDay",
"questionsExplicitlyGiven": false,
"questionAnswers": {
"length": 5,
"maxOffset": 90
}
},
```

where the variable length defines the length of questionSet or the number of questions.

## Comments

## Post a Comment