1+ <?php
2+
3+ namespace Tools ;
4+
5+
6+ use mysqli ;
7+ use mysqli_result ;
8+
9+ /**
10+ * Dump Parser
11+ *
12+ * A parsing object used by the processDump script
13+ *
14+ * @package Tools
15+ */
16+ class Parser {
17+
18+ private $ dbConn ;
19+ public $ debugMode ;
20+ private $ quietMode ;
21+
22+ public function __construct (mysqli $ dbConn , array $ arguments ) {
23+ $ this ->dbConn = $ dbConn ;
24+ $ this ->debugMode = in_array ("-d " , $ arguments );
25+ $ this ->quietMode = in_array ("-q " , $ arguments );
26+ if (in_array ("-c " , $ arguments )) {
27+ // Cleanup mode cleans up old partial parses
28+ $ this ->cleanup ();
29+ die ();
30+ }
31+ }
32+
33+ function cleanup () {
34+ // Emit a debug message
35+ $ this ->debug ("... Cleaning up temporary tables " );
36+
37+ // Drop the temporary tables
38+ if (!mysqli_query ($ this ->dbConn , "DROP TABLE classes " )) {
39+ echo ("*** Failed to drop table classes (ignored) \n" );
40+ echo (" " . mysqli_error ($ this ->dbConn ) . "\n" );
41+ }
42+ if (!mysqli_query ($ this ->dbConn , "DROP TABLE meeting " )) {
43+ echo ("*** Failed to drop table meeting (ignored) \n" );
44+ echo (" " . mysqli_error ($ this ->dbConn ) . "\n" );
45+ }
46+ if (!mysqli_query ($ this ->dbConn , "DROP TABLE instructors " )) {
47+ echo ("*** Failed to drop table instructor (ignored) \n" );
48+ echo (" " . mysqli_error ($ this ->dbConn ) . "\n" );
49+ }
50+ }
51+
52+ function debug ($ str , $ nl = true ) {
53+ if ($ this ->debugMode ) {
54+ echo ($ str . (($ nl ) ? "\n" : "" ));
55+ }
56+ }
57+
58+ function cleanupExtraResults ($ dbConn ) {
59+ // While there are more results, free them
60+ while (mysqli_next_result ($ dbConn )) {
61+ $ set = mysqli_use_result ($ dbConn );
62+ if ($ set instanceof mysqli_result) {
63+ mysqli_free_result ($ set );
64+ }
65+ }
66+ }
67+
68+ /**
69+ * Outputs error messages, cleans up temporaray tables, then dies
70+ * NOTE: Halts execution via die();
71+ * @param $messages mixed Array of messages to output
72+ */
73+ function halt ($ messages ) {
74+ // Iterate over the messages, cleanup and die
75+ if (is_array ($ messages )) {
76+ foreach ($ messages as $ message ) {
77+ echo "*** {$ message }\n" ;
78+ }
79+ } else {
80+ echo "*** {$ messages }\n" ;
81+ }
82+ $ this ->cleanup ();
83+ die ();
84+ }
85+
86+ /**
87+ * Inserts or updates a course. This function calls the stored procedure for
88+ * inserting or updating a course.
89+ * @param $quarter int The term that the course is in
90+ * @param $departCode string The code of the department
91+ * @param $classCode string The code for the class
92+ * @param $course int The number of the course
93+ * @param $credits int The credits the course offers
94+ * @param $title string The title of the course
95+ * @param $description string The description for the course
96+ * @return mixed String of error message returned on failure.
97+ * Integer of course ID returned on success
98+ */
99+ function insertOrUpdateCourse (int $ quarter , string $ departCode , string $ classCode , int $ course , int $ credits , string $ title , string $ description ) {
100+ global $ coursesUpdated , $ coursesAdded ;
101+ // Call the stored proc
102+ // TODO: Refactor out department ID number (0000)
103+ $ query = "CALL InsertOrUpdateCourse( {$ quarter }, 0000, ' {$ classCode }', ' {$ course }', {$ credits }, ' {$ title }', ' {$ description }') " ;
104+ $ success = mysqli_multi_query ($ this ->dbConn , $ query );
105+
106+ // Catch errors or return the id
107+ if (!$ success ) {
108+ // If the class code errors out, try the department code
109+ // TODO: Refactor out department ID number (0000)
110+ $ query = "CALL InsertOrUpdateCourse( {$ quarter }, 0000, ' {$ departCode }', ' {$ course }', {$ credits }, ' {$ title }', ' {$ description }') " ;
111+ $ success = mysqli_multi_query ($ this ->dbConn , $ query );
112+ if (!$ success ) {
113+ return mysqli_error ($ this ->dbConn );
114+ }
115+ }
116+
117+ // First result set is updated vs inserted
118+ $ actionSet = mysqli_store_result ($ this ->dbConn );
119+ $ action = mysqli_fetch_assoc ($ actionSet );
120+ if ($ action ['action ' ] == "updated " ) {
121+ $ coursesUpdated ++;
122+ } else {
123+ $ coursesAdded ++;
124+ }
125+ mysqli_free_result ($ actionSet );
126+
127+ // Second set is the id of the course
128+ mysqli_next_result ($ this ->dbConn );
129+ $ idSet = mysqli_store_result ($ this ->dbConn );
130+ $ id = mysqli_fetch_assoc ($ idSet );
131+
132+ // Free up the other calls
133+ mysqli_free_result ($ idSet );
134+ $ this ->cleanupExtraResults ($ this ->dbConn );
135+
136+ return $ id ['id ' ];
137+ }
138+
139+ function insertOrUpdateSection ($ courseId , $ section , $ title , $ instructor , $ type , $ status , $ maxenroll , $ curenroll ) {
140+ global $ sectUpdated , $ sectAdded ;
141+
142+ // Query to call the stored proc
143+ $ query = "CALL InsertOrUpdateSection( {$ courseId }, ' {$ section }', ' {$ title }', ' {$ instructor }', ' {$ type }', ' {$ status }', " ;
144+ $ query .= "{$ maxenroll }, {$ curenroll }) " ;
145+
146+ // Error check
147+ if (!mysqli_multi_query ($ this ->dbConn , $ query )) {
148+ return mysqli_error ($ this ->dbConn );
149+ }
150+
151+ // First result is the action performed
152+ $ actionSet = mysqli_store_result ($ this ->dbConn );
153+ $ action = mysqli_fetch_assoc ($ actionSet );
154+ if ($ action ['action ' ] == "updated " ) {
155+ $ sectUpdated ++;
156+ } else {
157+ $ sectAdded ++;
158+ }
159+ mysqli_free_result ($ actionSet );
160+
161+ // Second result is the
162+ mysqli_next_result ($ this ->dbConn );
163+ $ idSet = mysqli_store_result ($ this ->dbConn );
164+ $ id = mysqli_fetch_assoc ($ idSet );
165+
166+ // Free up other results
167+ mysqli_free_result ($ idSet );
168+ $ this ->cleanupExtraResults ($ this ->dbConn );
169+
170+ return $ id ['id ' ];
171+ }
172+
173+ function getTempSections ($ courseNum , $ offerNum , $ term , $ sessionNum ) {
174+ // Query for the sections of the course
175+ $ query = "SELECT class_section,descr,topic,enrl_stat,class_stat,class_type,enrl_cap,enrl_tot,instruction_mode,schedule_print " ;
176+ $ query .= "FROM classes WHERE crse_id= {$ courseNum } AND crse_offer_nbr= {$ offerNum } AND strm= {$ term } " ;
177+ $ query .= "AND session_code=' {$ sessionNum }' " ;
178+ $ results = mysqli_query ($ this ->dbConn , $ query );
179+
180+ // Check for errors
181+ if (!$ results ) {
182+ return mysqli_error ($ this ->dbConn );
183+ }
184+
185+ // Turn the results into an array of results
186+ // @TODO: Can we do this with fetch_all? Do we have mysql_nd?
187+ $ list = [];
188+ while ($ row = mysqli_fetch_assoc ($ results )) {
189+ $ list [] = $ row ;
190+ }
191+ return $ list ;
192+ }
193+
194+ function fileToTempTable (string $ tableName , $ file , $ fields , $ fileSize , string $ procFunc = null ) {
195+ // Process the file
196+ $ procBytes = 0 ;
197+ $ outPercent = [0 ];
198+ $ this ->debug ("... Copying {$ tableName } file to temporary table \n0% " , false );
199+ while ($ str = fgets ($ file , 4096 )) {
200+ // Trim those damn newlines
201+ $ str = trim ($ str );
202+
203+ // Progress bar
204+ if ($ this ->debugMode ) {
205+ $ percent = floor (($ procBytes / $ fileSize ) * 100 );
206+ if ($ percent % 10 == 0 && !in_array ($ percent , $ outPercent )) {
207+ $ outPercent [] = $ percent ;
208+ echo ("... {$ percent }% " );
209+ }
210+ }
211+
212+ // If we don't have 23 pipes, then we need to read another line
213+ $ lineSplit = explode ("| " , $ str );
214+ while (count ($ lineSplit ) < $ fields + 1 ) {
215+ $ str .= fgets ($ file , 4096 );
216+ $ lineSplit = explode ("| " , $ str );
217+ }
218+ $ procBytes += strlen ($ str ) + 1 ;
219+
220+ // If we don't have $fields+1 fields, shit's borked
221+ if (count ($ lineSplit ) != $ fields + 1 ) {
222+ echo ("*** Malformed line {$ fields }, " . count ($ lineSplit ) . "\n" );
223+ echo ($ str . "\n" );
224+ continue ;
225+ }
226+
227+ // We only need the first $fields, otherwise imploding will break
228+ $ lineSplit = array_splice ($ lineSplit , 0 , $ fields , true );
229+
230+ // Call the special attribute processing function
231+ if ($ procFunc ) {
232+ $ lineSplit = call_user_func ([$ this , $ procFunc ], $ lineSplit );
233+ if ($ lineSplit === false ) {
234+ // The proc function doesn't want us to proceed with
235+ // this line
236+ continue ;
237+ }
238+ }
239+
240+ // Build a query
241+ $ insQuery = "INSERT INTO {$ tableName } VALUES(' " . implode ("', ' " , $ lineSplit ) . "') " ;
242+ if (!mysqli_query ($ this ->dbConn , $ insQuery )) {
243+ echo ("*** Failed to insert {$ tableName }\n" );
244+ echo (" " . mysqli_error ($ this ->dbConn ) . "\n" );
245+ continue ;
246+ }
247+ }
248+
249+ $ this ->debug ("...100% " );
250+ }
251+
252+ // Process the class file
253+ function procClassArray (array $ lineSplit ): array {
254+ // Escape class title, description, and course number (since it needs to be trimmed)
255+ $ lineSplit [6 ] = $ this ->dbConn ->real_escape_string (trim ($ lineSplit [6 ]));
256+ $ lineSplit [7 ] = $ this ->dbConn ->real_escape_string ($ lineSplit [7 ]);
257+ $ lineSplit [8 ] = $ this ->dbConn ->real_escape_string (trim ($ lineSplit [8 ]));
258+ $ lineSplit [23 ] = $ this ->dbConn ->real_escape_string ($ lineSplit [23 ]);
259+
260+ // Grab the integer credit count (they give it to us as a decimal)
261+ preg_match ('/(\d)+\.\d\d/ ' , $ lineSplit [11 ], $ match );
262+ $ lineSplit [11 ] = $ match [1 ];
263+
264+ // Make the section number at least 2 digits
265+ $ lineSplit [4 ] = str_pad ($ lineSplit [4 ], 2 , '0 ' , STR_PAD_LEFT );
266+ return $ lineSplit ;
267+ }
268+
269+ // Process the meeting pattern file
270+ function procMeetArray (array $ lineSplit ) {
271+ // Turn the start/end times from 03:45 PM to 154500
272+ // Hours must be mod'd by 12 so 12:00 PM does not become
273+ // 24:00 and 12 AM does not become 12:00
274+ $ timePreg = "/(\d\d):(\d\d) ([A-Z]{2})/ " ;
275+ if (!preg_match ($ timePreg , $ lineSplit [10 ], $ start ) || !preg_match ($ timePreg , $ lineSplit [11 ], $ end )) {
276+ // Odds are the class is TBD (which means we can't represent it)
277+ return false ;
278+ }
279+ $ lineSplit [10 ] = (($ start [3 ] == 'PM ' ) ? ($ start [1 ] % 12 ) + 12 : $ start [1 ] % 12 ) . $ start [2 ] . "00 " ;
280+ $ lineSplit [11 ] = (($ end [3 ] == 'PM ' ) ? ($ end [1 ] % 12 ) + 12 : $ end [1 ] % 12 ) . $ end [2 ] . "00 " ;
281+
282+ // Section number needs to be padded to at least 2 digits
283+ $ lineSplit [4 ] = str_pad ($ lineSplit [4 ], 2 , '0 ' , STR_PAD_LEFT );
284+ return $ lineSplit ;
285+ }
286+
287+ function procInstrArray (array $ lineSplit ): array {
288+ // Escape the instructor names
289+ $ lineSplit [6 ] = mysqli_real_escape_string ($ this ->dbConn , $ lineSplit [6 ]);
290+ $ lineSplit [7 ] = mysqli_real_escape_string ($ this ->dbConn , $ lineSplit [7 ]);
291+
292+ // Section number needs to be padded to at lease 2 digits
293+ $ lineSplit [4 ] = str_pad ($ lineSplit [4 ], 2 , '0 ' , STR_PAD_LEFT );
294+ return $ lineSplit ;
295+ }
296+ }
0 commit comments