aca318e1 |
1 | <?php // $Id$ |
2 | |
3 | //////////////////////////////////////////////////////////////////// |
4 | /// format.php - Default format class for file imports/exports. // |
5 | /// // |
6 | /// Doesn't do everything on it's own -- it needs to be extended. // |
7 | //////////////////////////////////////////////////////////////////// |
8 | |
9 | // Included by import.php and export.php |
10 | |
f5565b69 |
11 | class qformat_default { |
aca318e1 |
12 | |
13 | var $displayerrors = true; |
14 | var $category = NULL; |
15 | var $course = NULL; |
16 | var $questionids = array(); |
17 | |
18 | // functions to indicate import/export functionality |
19 | // override to return true if implemented |
20 | |
21 | function provide_import() { |
22 | return false; |
23 | } |
24 | |
25 | function provide_export() { |
26 | return false; |
27 | } |
28 | |
29 | /// Importing functions |
30 | |
31 | function importpreprocess($category, $course=NULL ) { |
32 | /// Does any pre-processing that may be desired |
33 | |
34 | $this->category = $category; // Important |
35 | $this->course = $course; |
36 | |
37 | return true; |
38 | } |
39 | |
76f0a334 |
40 | /** |
41 | * |
42 | * @PARAM $matchgrades string 'error' or 'nearest', mismatched grades handling |
43 | */ |
44 | function importprocess($filename, $matchgrades='error') { |
aca318e1 |
45 | /// Processes a given file. There's probably little need to change this |
46 | |
47 | if (! $lines = $this->readdata($filename)) { |
1e3d6fd8 |
48 | notify( get_string('cannotread','quiz') ); |
aca318e1 |
49 | return false; |
50 | } |
51 | |
52 | if (! $questions = $this->readquestions($lines)) { // Extract all the questions |
1e3d6fd8 |
53 | notify( get_string('noquestionsinfile','quiz') ); |
aca318e1 |
54 | return false; |
55 | } |
56 | |
1e3d6fd8 |
57 | notify( get_string('importingquestions','quiz',count($questions)) ); |
aca318e1 |
58 | |
76f0a334 |
59 | // get list of valid answer grades |
60 | $grades = get_grade_options(); |
61 | $gradeoptionsfull = $grades->gradeoptionsfull; |
62 | |
aca318e1 |
63 | $count = 0; |
64 | |
65 | foreach ($questions as $question) { // Process and store each question |
66 | $count++; |
67 | |
68 | echo "<hr /><p><b>$count</b>. ".stripslashes($question->questiontext)."</p>"; |
69 | |
76f0a334 |
70 | // check for answer grades validity (must match fixed list of grades) |
71 | $fractions = $question->fraction; |
2d52056f |
72 | if (!empty($fractions)) { |
73 | $answersvalid = true; // in case they are! |
74 | foreach ($fractions as $key => $fraction) { |
75 | $newfraction = match_grade_options($gradeoptionsfull, $fraction, $matchgrades); |
76 | if ($newfraction===false) { |
77 | $answersvalid = false; |
78 | } |
79 | else { |
80 | $fractions[$key] = $newfraction; |
81 | } |
82 | } |
83 | if (!$answersvalid) { |
84 | notify( get_string('matcherror','quiz') ); |
85 | continue; |
76f0a334 |
86 | } |
87 | else { |
2d52056f |
88 | $question->fraction = $fractions; |
76f0a334 |
89 | } |
90 | } |
76f0a334 |
91 | |
aca318e1 |
92 | $question->category = $this->category->id; |
93 | $question->stamp = make_unique_id_code(); // Set the unique code (not to be changed) |
aca318e1 |
94 | |
062f1125 |
95 | if (!$question->id = insert_record("question", $question)) { |
1e3d6fd8 |
96 | error( get_string('cannotinsert','quiz') ); |
aca318e1 |
97 | } |
98 | |
99 | $this->questionids[] = $question->id; |
100 | |
101 | // Now to save all the answers and type-specific options |
102 | |
103 | global $QTYPES; |
104 | $result = $QTYPES[$question->qtype] |
105 | ->save_question_options($question); |
106 | |
107 | if (!empty($result->error)) { |
108 | notify($result->error); |
109 | return false; |
110 | } |
111 | |
112 | if (!empty($result->notice)) { |
113 | notify($result->notice); |
114 | return true; |
115 | } |
cbe20043 |
116 | |
117 | // Give the question a unique version stamp determined by question_hash() |
118 | set_field('question', 'version', question_hash($question), 'id', $question->id); |
aca318e1 |
119 | } |
120 | return true; |
121 | } |
122 | |
123 | |
124 | function readdata($filename) { |
125 | /// Returns complete file with an array, one item per line |
126 | |
127 | if (is_readable($filename)) { |
128 | $filearray = file($filename); |
129 | |
130 | /// Check for Macintosh OS line returns (ie file on one line), and fix |
131 | if (ereg("\r", $filearray[0]) AND !ereg("\n", $filearray[0])) { |
132 | return explode("\r", $filearray[0]); |
133 | } else { |
134 | return $filearray; |
135 | } |
136 | } |
137 | return false; |
138 | } |
139 | |
140 | function readquestions($lines) { |
141 | /// Parses an array of lines into an array of questions, |
142 | /// where each item is a question object as defined by |
143 | /// readquestion(). Questions are defined as anything |
144 | /// between blank lines. |
145 | |
146 | $questions = array(); |
147 | $currentquestion = array(); |
148 | |
149 | foreach ($lines as $line) { |
150 | $line = trim($line); |
151 | if (empty($line)) { |
152 | if (!empty($currentquestion)) { |
153 | if ($question = $this->readquestion($currentquestion)) { |
154 | $questions[] = $question; |
155 | } |
156 | $currentquestion = array(); |
157 | } |
158 | } else { |
159 | $currentquestion[] = $line; |
160 | } |
161 | } |
162 | |
163 | if (!empty($currentquestion)) { // There may be a final question |
164 | if ($question = $this->readquestion($currentquestion)) { |
165 | $questions[] = $question; |
166 | } |
167 | } |
168 | |
169 | return $questions; |
170 | } |
171 | |
172 | |
173 | function defaultquestion() { |
174 | // returns an "empty" question |
175 | // Somewhere to specify question parameters that are not handled |
176 | // by import but are required db fields. |
177 | // This should not be overridden. |
178 | $question = new stdClass(); |
179 | $question->shuffleanswers = 0; |
180 | $question->defaultgrade = 1; |
181 | $question->image = ""; |
182 | $question->usecase = 0; |
183 | $question->multiplier = array(); |
184 | |
185 | return $question; |
186 | } |
187 | |
188 | function readquestion($lines) { |
189 | /// Given an array of lines known to define a question in |
190 | /// this format, this function converts it into a question |
191 | /// object suitable for processing and insertion into Moodle. |
192 | |
1e3d6fd8 |
193 | $formatnotimplemented = get_string( 'formatnotimplemented','quiz' ); |
194 | echo "<p>$formatnotimplemented</p>"; |
aca318e1 |
195 | |
196 | return NULL; |
197 | } |
198 | |
199 | |
200 | function importpostprocess() { |
201 | /// Does any post-processing that may be desired |
202 | /// Argument is a simple array of question ids that |
203 | /// have just been added. |
204 | |
205 | return true; |
206 | } |
207 | |
208 | // Export functions |
209 | |
210 | |
211 | function export_file_extension() { |
212 | /// return the files extension appropriate for this type |
213 | /// override if you don't want .txt |
214 | |
215 | return ".txt"; |
216 | } |
217 | |
218 | function exportpreprocess($category, $course) { |
219 | /// Does any pre-processing that may be desired |
220 | |
221 | $this->category = $category; // Important |
222 | $this->course = $course; // As is this! |
223 | |
224 | return true; |
225 | } |
226 | |
227 | function presave_process( $content ) { |
228 | /// enables any processing to be done on the content |
229 | /// just prior to the file being saved |
230 | /// default is to do nothing |
231 | |
232 | return $content; |
233 | } |
234 | |
235 | function exportprocess($filename) { |
236 | /// Exports a given category. There's probably little need to change this |
237 | |
238 | global $CFG; |
239 | |
240 | // create a directory for the exports (if not already existing) |
241 | $dirname = get_string("exportfilename","quiz"); |
242 | $courseid = $this->course->id; |
243 | $path = $CFG->dataroot.'/'.$courseid.'/'.$dirname; |
244 | if (!is_dir($path)) { |
245 | if (!mkdir($path, $CFG->directorypermissions)) { |
1e3d6fd8 |
246 | error( get_string('cannotcreatepath','quiz',$path) ); |
aca318e1 |
247 | } |
248 | } |
249 | |
250 | // get the questions (from database) in this category |
251 | // only get q's with no parents (no cloze subquestions specifically) |
252 | $questions = get_questions_category( $this->category, true ); |
253 | |
1e3d6fd8 |
254 | notify( get_string('exportingquestions','quiz') ); |
aca318e1 |
255 | if (!count($questions)) { |
1e3d6fd8 |
256 | notify( get_string('noquestions','quiz') ); |
257 | return false; |
aca318e1 |
258 | } |
259 | $count = 0; |
260 | |
261 | // results are first written into string (and then to a file) |
262 | // so create/initialize the string here |
263 | $expout = ""; |
264 | |
265 | // iterate through questions |
266 | foreach($questions as $question) { |
267 | $count++; |
268 | $qtype = $question->qtype; |
269 | // ignore random questiond |
270 | if ($qtype!=RANDOM) { |
271 | echo "<hr /><p><b>$count</b>. ".stripslashes($question->questiontext)."</p>"; |
272 | $expout .= $this->writequestion( $question ) . "\n"; |
273 | } |
274 | } |
275 | |
276 | // final pre-process on exported data |
277 | $expout = $this->presave_process( $expout ); |
278 | |
279 | // write file |
280 | $filepath = $path."/".$filename . $this->export_file_extension(); |
281 | if (!$fh=fopen($filepath,"w")) { |
1e3d6fd8 |
282 | error( get_string('cannotopen','quiz',$filepath) ); |
aca318e1 |
283 | } |
284 | if (!fwrite($fh, $expout)) { |
1e3d6fd8 |
285 | error( get_string('cannotwrite','quiz',$filepath) ); |
aca318e1 |
286 | } |
287 | fclose($fh); |
288 | |
289 | return true; |
290 | } |
291 | |
292 | function exportpostprocess() { |
293 | /// Does any post-processing that may be desired |
294 | |
295 | return true; |
296 | } |
297 | |
298 | function writequestion($question) { |
299 | /// Turns a question object into textual output in the given format |
300 | /// must be overidden |
301 | |
1e3d6fd8 |
302 | // if not overidden, then this is an error. |
303 | $formatnotimplemented = get_string( 'formatnotimplemented','quiz' ); |
304 | echo "<p>$formatnotimplemented</p>"; |
aca318e1 |
305 | |
306 | return NULL; |
307 | } |
308 | |
309 | } |
310 | |
311 | ?> |