Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
98.18% |
54 / 55 |
|
88.89% |
8 / 9 |
CRAP | |
0.00% |
0 / 1 |
CsvReader | |
98.18% |
54 / 55 |
|
88.89% |
8 / 9 |
24 | |
0.00% |
0 / 1 |
__construct | |
85.71% |
6 / 7 |
|
0.00% |
0 / 1 |
4.05 | |||
getValueFromLine | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
2 | |||
loadFile | |
100.00% |
34 / 34 |
|
100.00% |
1 / 1 |
10 | |||
load | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
3 | |||
getInstructors | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
getCourses | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
getDepartments | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
getReserves | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
getErrors | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 |
1 | <?php |
2 | |
3 | /** |
4 | * Support class to build reserves data from CSV file(s). |
5 | * |
6 | * PHP version 8 |
7 | * |
8 | * Copyright (C) Villanova University 2010. |
9 | * |
10 | * This program is free software; you can redistribute it and/or modify |
11 | * it under the terms of the GNU General Public License version 2, |
12 | * as published by the Free Software Foundation. |
13 | * |
14 | * This program is distributed in the hope that it will be useful, |
15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
17 | * GNU General Public License for more details. |
18 | * |
19 | * You should have received a copy of the GNU General Public License |
20 | * along with this program; if not, write to the Free Software |
21 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
22 | * |
23 | * @category VuFind |
24 | * @package Reserves |
25 | * @author Demian Katz <demian.katz@villanova.edu> |
26 | * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License |
27 | * @link https://vufind.org/wiki Wiki |
28 | */ |
29 | |
30 | namespace VuFind\Reserves; |
31 | |
32 | use function count; |
33 | use function is_array; |
34 | |
35 | /** |
36 | * Support class to build reserves data from CSV file(s). |
37 | * |
38 | * @category VuFind |
39 | * @package Reserves |
40 | * @author Demian Katz <demian.katz@villanova.edu> |
41 | * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License |
42 | * @link https://vufind.org/wiki Wiki |
43 | */ |
44 | class CsvReader |
45 | { |
46 | /** |
47 | * Files to load |
48 | * |
49 | * @var array |
50 | */ |
51 | protected $files; |
52 | |
53 | /** |
54 | * CSV delimiter |
55 | * |
56 | * @var string |
57 | */ |
58 | protected $delimiter; |
59 | |
60 | /** |
61 | * Field template (value => index) |
62 | * |
63 | * @var array |
64 | */ |
65 | protected $template; |
66 | |
67 | /** |
68 | * Instructor data loaded from files |
69 | * |
70 | * @var array |
71 | */ |
72 | protected $instructors = []; |
73 | |
74 | /** |
75 | * Course data loaded from files |
76 | * |
77 | * @var array |
78 | */ |
79 | protected $courses = []; |
80 | |
81 | /** |
82 | * Department data loaded from files |
83 | * |
84 | * @var array |
85 | */ |
86 | protected $departments = []; |
87 | |
88 | /** |
89 | * Reserves data loaded from files |
90 | * |
91 | * @var array |
92 | */ |
93 | protected $reserves = []; |
94 | |
95 | /** |
96 | * Flag indicating whether or not we have processed data yet. |
97 | * |
98 | * @var bool |
99 | */ |
100 | protected $loaded = false; |
101 | |
102 | /** |
103 | * Error messages collected during loading. |
104 | * |
105 | * @var string |
106 | */ |
107 | protected $errors = ''; |
108 | |
109 | /** |
110 | * Constructor |
111 | * |
112 | * @param array|string $files Array of files to load (or single filename). |
113 | * @param string $delimiter Delimiter used by file(s). |
114 | * @param string $template Template showing field positions within |
115 | * file(s). Comma-separated list containing BIB_ID, INSTRUCTOR, COURSE, |
116 | * DEPARTMENT and/or SKIP. Default = BIB_ID,COURSE,INSTRUCTOR,DEPARTMENT |
117 | * |
118 | * @throws \Exception |
119 | */ |
120 | public function __construct($files, $delimiter = ',', $template = null) |
121 | { |
122 | $this->files = is_array($files) ? $files : [$files]; |
123 | $this->delimiter = $delimiter; |
124 | |
125 | // Provide default template if none passed in: |
126 | if (null === $template) { |
127 | $template = 'BIB_ID,COURSE,INSTRUCTOR,DEPARTMENT'; |
128 | } |
129 | |
130 | // Convert template from comma-delimited list to map of name => index: |
131 | $this->template = array_flip(array_map('trim', explode(',', $template))); |
132 | |
133 | if (!isset($this->template['BIB_ID'])) { |
134 | throw new \Exception('Template must include BIB_ID field.'); |
135 | } |
136 | } |
137 | |
138 | /** |
139 | * Load the appropriate data field from the line using our template. |
140 | * |
141 | * @param array $line CSV row |
142 | * @param string $key Value to load |
143 | * |
144 | * @return string |
145 | */ |
146 | protected function getValueFromLine($line, $key) |
147 | { |
148 | return isset($this->template[$key]) ? $line[$this->template[$key]] : ''; |
149 | } |
150 | |
151 | /** |
152 | * Load data from a single file. |
153 | * |
154 | * @param string $fn Filename |
155 | * |
156 | * @return void |
157 | * @throws \Exception |
158 | */ |
159 | protected function loadFile($fn) |
160 | { |
161 | if (!file_exists($fn) || !($fh = fopen($fn, 'r'))) { |
162 | throw new \Exception("Could not open $fn!"); |
163 | } |
164 | $lineNo = $goodLines = 0; |
165 | while ($line = fgetcsv($fh, 0, $this->delimiter)) { |
166 | $lineNo++; |
167 | |
168 | if (count($line) < count($this->template)) { |
169 | $this->errors .= "Skipping incomplete row: $fn, line $lineNo\n"; |
170 | continue; |
171 | } |
172 | |
173 | $instructor = $this->getValueFromLine($line, 'INSTRUCTOR'); |
174 | if (!empty($instructor)) { |
175 | $this->instructors[$instructor] = $instructor; |
176 | } |
177 | |
178 | $course = $this->getValueFromLine($line, 'COURSE'); |
179 | if (!empty($course)) { |
180 | $this->courses[$course] = $course; |
181 | } |
182 | |
183 | $department = $this->getValueFromLine($line, 'DEPARTMENT'); |
184 | if (!empty($department)) { |
185 | $this->departments[$department] = $department; |
186 | } |
187 | |
188 | $bibId = trim($line[$this->template['BIB_ID']]); |
189 | if ($bibId == '') { |
190 | $this->errors |
191 | .= "Skipping empty/missing Bib ID: $fn, line $lineNo\n"; |
192 | continue; |
193 | } |
194 | |
195 | $goodLines++; |
196 | $this->reserves[] = [ |
197 | 'BIB_ID' => $bibId, |
198 | 'INSTRUCTOR_ID' => $instructor, |
199 | 'COURSE_ID' => $course, |
200 | 'DEPARTMENT_ID' => $department, |
201 | ]; |
202 | } |
203 | fclose($fh); |
204 | if ($goodLines == 0) { |
205 | throw new \Exception( |
206 | "Could not find valid data. Details:\n" . trim($this->errors) |
207 | ); |
208 | } |
209 | } |
210 | |
211 | /** |
212 | * Load data if it is not already loaded. |
213 | * |
214 | * @return void |
215 | * @throws \Exception |
216 | */ |
217 | protected function load() |
218 | { |
219 | // Only load data if we haven't already retrieved it. |
220 | if (!$this->loaded) { |
221 | foreach ($this->files as $fn) { |
222 | $this->loadFile($fn); |
223 | } |
224 | |
225 | $this->loaded = true; |
226 | } |
227 | } |
228 | |
229 | /** |
230 | * Get instructor data |
231 | * |
232 | * @return array |
233 | * @throws \Exception |
234 | */ |
235 | public function getInstructors() |
236 | { |
237 | $this->load(); |
238 | return $this->instructors; |
239 | } |
240 | |
241 | /** |
242 | * Get course data |
243 | * |
244 | * @return array |
245 | * @throws \Exception |
246 | */ |
247 | public function getCourses() |
248 | { |
249 | $this->load(); |
250 | return $this->courses; |
251 | } |
252 | |
253 | /** |
254 | * Get department data |
255 | * |
256 | * @return array |
257 | * @throws \Exception |
258 | */ |
259 | public function getDepartments() |
260 | { |
261 | $this->load(); |
262 | return $this->departments; |
263 | } |
264 | |
265 | /** |
266 | * Get reserves data |
267 | * |
268 | * @return array |
269 | * @throws \Exception |
270 | */ |
271 | public function getReserves() |
272 | { |
273 | $this->load(); |
274 | return $this->reserves; |
275 | } |
276 | |
277 | /** |
278 | * Get collected error messages |
279 | * |
280 | * @return string |
281 | */ |
282 | public function getErrors() |
283 | { |
284 | return $this->errors; |
285 | } |
286 | } |