import_csv.php 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554
  1. <?php
  2. if (PHP_SAPI!='cli') {
  3. die('Run this script through the command line or comment this line in the code');
  4. }
  5. require_once __DIR__.'/../inc/global.inc.php';
  6. require_once api_get_path(LIBRARY_PATH).'log.class.php';
  7. /**
  8. * Class ImportCsv
  9. */
  10. class ImportCsv
  11. {
  12. private $logger;
  13. private $dumpValues;
  14. public $test;
  15. public $defaultLanguage = 'english';
  16. /**
  17. * @param Logger $logger
  18. */
  19. public function __construct($logger)
  20. {
  21. $this->logger = $logger;
  22. }
  23. /**
  24. * @param bool $dump
  25. */
  26. function setDumpValues($dump)
  27. {
  28. $this->dumpValues = $dump;
  29. }
  30. function getDumpValues()
  31. {
  32. return $this->dumpValues;
  33. }
  34. /**
  35. * Runs the import process
  36. */
  37. public function run()
  38. {
  39. $path = api_get_path(SYS_CODE_PATH).'cron/incoming/';
  40. if (!is_dir($path)) {
  41. echo "The folder! $path does not exits";
  42. exit;
  43. }
  44. if ($this->getDumpValues()) {
  45. $table = Database::get_main_table(TABLE_MAIN_USER);
  46. $sql = "DELETE FROM $table WHERE username NOT IN ('admin') AND lastname <> 'Anonymous' ";
  47. Database::query($sql);
  48. echo 'Dumping tables'.PHP_EOL;
  49. echo $sql.PHP_EOL;
  50. $table = Database::get_main_table(TABLE_MAIN_COURSE);
  51. $sql = "DELETE FROM $table";
  52. Database::query($sql);
  53. echo $sql.PHP_EOL;
  54. $table = Database::get_main_table(TABLE_MAIN_COURSE_USER);
  55. $sql = "DELETE FROM $table";
  56. Database::query($sql);
  57. echo $sql.PHP_EOL;
  58. $table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
  59. $sql = "DELETE FROM $table";
  60. Database::query($sql);
  61. echo $sql.PHP_EOL;
  62. $table = Database::get_main_table(TABLE_MAIN_SESSION);
  63. $sql = "DELETE FROM $table";
  64. Database::query($sql);
  65. echo $sql.PHP_EOL;
  66. $table = Database::get_main_table(TABLE_MAIN_SESSION_CATEGORY);
  67. $sql = "DELETE FROM $table";
  68. Database::query($sql);
  69. echo $sql.PHP_EOL;
  70. $table = Database::get_main_table(TABLE_MAIN_SESSION_COURSE);
  71. $sql = "DELETE FROM $table";
  72. Database::query($sql);
  73. echo $sql.PHP_EOL;
  74. $table = Database::get_main_table(TABLE_MAIN_SESSION_USER);
  75. $sql = "DELETE FROM $table";
  76. Database::query($sql);
  77. echo $sql.PHP_EOL;
  78. }
  79. echo "Starting with reading the files: ".PHP_EOL.PHP_EOL;
  80. $files = scandir($path);
  81. $fileToProcess = array();
  82. if (!empty($files)) {
  83. foreach ($files as $file) {
  84. $fileInfo = pathinfo($file);
  85. if ($fileInfo['extension'] == 'csv') {
  86. // teachers_yyyymmdd.csv, courses_yyyymmdd.csv, students_yyyymmdd.csv and sessions_yyyymmdd.csv
  87. $parts = explode('_', $fileInfo['filename']);
  88. $method = 'import'.ucwords($parts[1]);
  89. if (method_exists($this, $method)) {
  90. $fileToProcess[$parts[1]][] = array(
  91. 'method' => $method,
  92. 'file' => $path.$fileInfo['basename']
  93. );
  94. //$this->$method($path.$fileInfo['basename']);
  95. } else {
  96. echo "Error - This file '$file' can't be processed.".PHP_EOL;
  97. echo "The file have to has this format:".PHP_EOL;
  98. echo "prefix_students_ddmmyyyy.csv, prefix_teachers_ddmmyyyy.csv, prefix_courses_ddmmyyyy.csv, prefix_sessions_ddmmyyyy.csv ".PHP_EOL;
  99. exit;
  100. }
  101. }
  102. }
  103. if (empty($fileToProcess)) {
  104. echo 'Error - no files to process.';
  105. exit;
  106. }
  107. $sections = array('students', 'teachers', 'courses', 'sessions');
  108. $this->prepareImport();
  109. foreach ($sections as $section) {
  110. $this->logger->addInfo("-- Import $section --");
  111. if (isset($fileToProcess[$section]) && !empty($fileToProcess[$section])) {
  112. $files = $fileToProcess[$section];
  113. foreach ($files as $fileInfo) {
  114. $method = $fileInfo['method'];
  115. $file = $fileInfo['file'];
  116. echo 'Reading file: '.$file.PHP_EOL;
  117. $this->logger->addInfo("Reading file: $file");
  118. $this->$method($file);
  119. }
  120. }
  121. }
  122. }
  123. }
  124. private function prepareImport()
  125. {
  126. // Create user extra field: extra_external_user_id
  127. UserManager::create_extra_field('external_user_id', 1, 'External user id', null);
  128. // Create course extra field: extra_external_course_id
  129. CourseManager::create_course_extra_field('external_course_id', 1, 'External course id');
  130. // Create session extra field extra_external_session_id
  131. SessionManager::create_session_extra_field('external_session_id', 1, 'External session id');
  132. }
  133. /**
  134. * @param string $file
  135. */
  136. function moveFile($file)
  137. {
  138. $moved = str_replace('incoming', 'treated', $file);
  139. if ($this->test) {
  140. $result = 1;
  141. } else {
  142. $result = rename($file, $moved);
  143. }
  144. if ($result) {
  145. $this->logger->addInfo("Moving file to the treated folder: $file");
  146. } else {
  147. $this->logger->addError("Error - Cant move file to the treated folder: $file");
  148. }
  149. }
  150. /**
  151. * @param array $row
  152. *
  153. * @return array
  154. */
  155. private function cleanUserRow($row)
  156. {
  157. $row['lastname'] = $row['LastName'];
  158. $row['firstname'] = $row['FirstName'];
  159. $row['email'] = $row['Email'];
  160. $row['username'] = $row['UserName'];
  161. $row['password'] = $row['Password'];
  162. $row['auth_srouce'] = $row['AuthSource'];
  163. $row['official_code'] = $row['OfficialCode'];
  164. $row['phone'] = $row['PhoneNumber'];
  165. if (isset($row['StudentID'])) {
  166. $row['extra_external_user_id'] = $row['StudentID'];
  167. }
  168. if (isset($row['TeacherID'])) {
  169. $row['extra_external_user_id'] = $row['TeacherID'];
  170. }
  171. //$row['lastname'] = Status
  172. return $row;
  173. }
  174. /**
  175. * @param array $row
  176. * @return array
  177. */
  178. private function cleanCourseRow($row)
  179. {
  180. $row['title'] = $row['Title'];
  181. $row['course_code'] = $row['Code'];
  182. $row['category_code'] = $row['CourseCategory'];
  183. $row['email'] = $row['Teacher'];
  184. $row['language'] = $row['Language'];
  185. $row['external_id'] = $row['CourseID'];
  186. if (isset($row['CourseID'])) {
  187. $row['extra_external_course_id'] = $row['CourseID'];
  188. }
  189. return $row;
  190. }
  191. /**
  192. * File to import
  193. * @param string $file
  194. */
  195. private function importTeachers($file)
  196. {
  197. $data = Import::csv_to_array($file);
  198. /* Unique identifier: official-code username.
  199. Email address and password should never get updated. *ok
  200. The only fields that I can think of that should update if the data changes in the csv file are FirstName and LastName. *ok
  201. A slight edit of these fields should be taken into account. ???
  202. Adding teachers is no problem, but deleting them shouldn’t be automated, but we should get a log of “to delete teachers”.
  203. We’ll handle that manually if applicable.
  204. No delete!
  205. */
  206. $language = $this->defaultLanguage;
  207. if (!empty($data)) {
  208. $this->logger->addInfo(count($data)." records found.");
  209. foreach ($data as $row) {
  210. $row = $this->cleanUserRow($row);
  211. $userInfo = api_get_user_info_from_username($row['username']);
  212. $userInfoByOfficialCode = api_get_user_info_from_official_code($row['official_code']);
  213. if (empty($userInfo) && empty($userInfoByOfficialCode)) {
  214. // Create user
  215. $userId = UserManager::create_user(
  216. $row['firstname'],
  217. $row['lastname'],
  218. COURSEMANAGER,
  219. $row['email'],
  220. $row['username'],
  221. $row['password'],
  222. $row['official_code'],
  223. $language, //$row['language'],
  224. $row['phone'],
  225. null, //$row['picture'], //picture
  226. PLATFORM_AUTH_SOURCE, // ?
  227. '0000-00-00 00:00:00', //$row['expiration_date'], //$expiration_date = '0000-00-00 00:00:00',
  228. 1, //active
  229. 0,
  230. null, // extra
  231. null, //$encrypt_method = '',
  232. false //$send_mail = false
  233. );
  234. if ($userId) {
  235. foreach ($row as $key => $value) {
  236. if (substr($key, 0, 6) == 'extra_') { //an extra field
  237. UserManager::update_extra_field_value($userId, substr($key, 6), $value);
  238. }
  239. }
  240. $this->logger->addInfo("Teachers - User created: ".$row['username']);
  241. } else {
  242. $this->logger->addError("Teachers - User NOT created: ".$row['username']." ".$row['firstname']." ".$row['lastname']);
  243. }
  244. } else {
  245. if (empty($userInfo)) {
  246. $this->logger->addError("Teachers - Can't update user :".$row['username']);
  247. continue;
  248. }
  249. // Update user
  250. $result = UserManager::update_user(
  251. $userInfo['user_id'],
  252. $row['firstname'], // <<-- changed
  253. $row['lastname'], // <<-- changed
  254. $userInfo['username'],
  255. null, //$password = null,
  256. $auth_source = null,
  257. $userInfo['email'],
  258. COURSEMANAGER,
  259. $userInfo['official_code'],
  260. $userInfo['phone'],
  261. $userInfo['picture_uri'],
  262. $userInfo['expiration_date'],
  263. $userInfo['active'],
  264. null, //$creator_id = null,
  265. 0, //$hr_dept_id = 0,
  266. null, // $extra = null,
  267. null, //$language = 'english',
  268. null, //$encrypt_method = '',
  269. false, //$send_email = false,
  270. 0 //$reset_password = 0
  271. );
  272. if ($result) {
  273. foreach ($row as $key => $value) {
  274. if (substr($key, 0, 6) == 'extra_') { //an extra field
  275. UserManager::update_extra_field_value($userInfo['user_id'], substr($key, 6), $value);
  276. }
  277. }
  278. $this->logger->addInfo("Teachers - User updated: ".$row['username']);
  279. } else {
  280. $this->logger->addError("Teachers - User not updated: ".$row['username']);
  281. }
  282. }
  283. }
  284. }
  285. $this->moveFile($file);
  286. }
  287. /**
  288. * @param string $file
  289. */
  290. private function importStudents($file)
  291. {
  292. $data = Import::csv_to_array($file);
  293. /*
  294. * Another users import.
  295. Unique identifier: official code and username . ok
  296. Username and password should never get updated. ok
  297. If an update should need to occur (because it changed in the .csv), we’ll want that logged. We will handle this manually in that case.
  298. All other fields should be updateable, though passwords should of course not get updated. ok
  299. If a user gets deleted (not there anymore),
  300. He should be set inactive one year after the current date. So I presume you’ll just update the expiration date. We want to grant access to courses up to a year after deletion.
  301. */
  302. if (!empty($data)) {
  303. $language = $this->defaultLanguage;
  304. $this->logger->addInfo(count($data)." records found.");
  305. foreach ($data as $row) {
  306. $row = $this->cleanUserRow($row);
  307. $userInfo = api_get_user_info_from_username($row['username']);
  308. $userInfoByOfficialCode = api_get_user_info_from_official_code($row['official_code']);
  309. if (empty($userInfo) && empty($userInfoByOfficialCode)) {
  310. // Create user
  311. $result = UserManager::create_user(
  312. $row['firstname'],
  313. $row['lastname'],
  314. COURSEMANAGER,
  315. $row['email'],
  316. $row['username'],
  317. $row['password'],
  318. $row['official_code'],
  319. $language, //$row['language'],
  320. $row['phone'],
  321. null, //$row['picture'], //picture
  322. PLATFORM_AUTH_SOURCE, // ?
  323. '0000-00-00 00:00:00', //$row['expiration_date'], //$expiration_date = '0000-00-00 00:00:00',
  324. 1, //active
  325. 0,
  326. null, // extra
  327. null, //$encrypt_method = '',
  328. false //$send_mail = false
  329. );
  330. if ($result) {
  331. foreach ($row as $key => $value) {
  332. if (substr($key, 0, 6) == 'extra_') { //an extra field
  333. UserManager::update_extra_field_value($result, substr($key, 6), $value);
  334. }
  335. }
  336. $this->logger->addInfo("Students - User created: ".$row['username']);
  337. } else {
  338. $this->logger->addError("Students - User NOT created: ".$row['username']." ".$row['firstname']." ".$row['lastname']);
  339. }
  340. } else {
  341. if (empty($userInfo)) {
  342. $this->logger->addError("Students - Can't update user :".$row['username']);
  343. continue;
  344. }
  345. if ($row['action'] == 'delete') {
  346. // INactive one year later
  347. $userInfo['expiration_date'] = api_get_utc_datetime(api_strtotime(time() + 365*24*60*60));
  348. }
  349. // Update user
  350. $result = UserManager::update_user(
  351. $userInfo['user_id'],
  352. $row['firstname'], // <<-- changed
  353. $row['lastname'], // <<-- changed
  354. $userInfo['username'],
  355. null, //$password = null,
  356. $auth_source = null,
  357. $userInfo['email'],
  358. STUDENT,
  359. $userInfo['official_code'],
  360. $userInfo['phone'],
  361. $userInfo['picture_uri'],
  362. $userInfo['expiration_date'],
  363. $userInfo['active'],
  364. null, //$creator_id = null,
  365. 0, //$hr_dept_id = 0,
  366. null, // $extra = null,
  367. null, //$language = 'english',
  368. null, //$encrypt_method = '',
  369. false, //$send_email = false,
  370. 0 //$reset_password = 0
  371. );
  372. if ($result) {
  373. foreach ($row as $key => $value) {
  374. if (substr($key, 0, 6) == 'extra_') { //an extra field
  375. UserManager::update_extra_field_value($userInfo['user_id'], substr($key, 6), $value);
  376. }
  377. }
  378. $this->logger->addInfo("Students - User updated: ".$row['username']);
  379. } else {
  380. $this->logger->addError("Students - User NOT updated: ".$row['username']." ".$row['firstname']." ".$row['lastname']);
  381. }
  382. }
  383. }
  384. }
  385. $this->moveFile($file);
  386. }
  387. /**
  388. * @param string $file
  389. */
  390. private function importCourses($file)
  391. {
  392. $data = Import::csv_to_array($file);
  393. $language = $this->defaultLanguage;
  394. if (!empty($data)) {
  395. $this->logger->addInfo(count($data)." records found.");
  396. foreach ($data as $row) {
  397. $row = $this->cleanCourseRow($row);
  398. $courseInfo = api_get_course_info($row['course_code']);
  399. if (empty($courseInfo)) {
  400. // Create
  401. $params = array();
  402. $params['title'] = $row['title'];
  403. $params['exemplary_content'] = false;
  404. $params['wanted_code'] = $row['course_code'];
  405. $params['category_code'] = $row['category_code'];
  406. $params['course_language'] = $row['language'];
  407. //$params['gradebook_model_id'] = isset($course_values['gradebook_model_id']) ? $course_values['gradebook_model_id'] : null;
  408. $courseInfo = CourseManager::create_course($params);
  409. if (!empty($courseInfo)) {
  410. CourseManager::update_course_extra_field_value($courseInfo['code'], 'external_course_id', $row['extra_external_course_id']);
  411. $this->logger->addInfo("Courses - Course created ".$courseInfo['code']);
  412. } else {
  413. $this->logger->addError("Courses - Can't create course:".$row['title']);
  414. }
  415. } else {
  416. // Update
  417. $params = array(
  418. 'title' => $row['title'],
  419. );
  420. $result = CourseManager::update_attributes($courseInfo['id'], $params);
  421. if ($result) {
  422. $this->logger->addInfo("Courses - Course updated ".$courseInfo['code']);
  423. } else {
  424. $this->logger->addError("Courses - Course NOT updated ".$courseInfo['code']);
  425. }
  426. }
  427. }
  428. }
  429. $this->moveFile($file);
  430. }
  431. /**
  432. * @param string $file
  433. */
  434. private function importSessions($file)
  435. {
  436. $result = SessionManager::importCSV($file, true, 1, $this->logger, array('SessionId' => 'extra_external_session_id'));
  437. if (!empty($result['error_message'])) {
  438. $this->logger->addError($result['error_message']);
  439. }
  440. $this->logger->addInfo("Sessions - Sessions parsed: ".$result['session_counter']);
  441. $this->moveFile($file);
  442. }
  443. }
  444. use Monolog\Logger;
  445. use Monolog\Handler\StreamHandler;
  446. use Monolog\Handler\NativeMailerHandler;
  447. use Monolog\Handler\RotatingFileHandler;
  448. use Monolog\Handler\BufferHandler;
  449. $logger = new Logger('cron');
  450. $emails = isset($_configuration['cron_notification_mails']) ? $_configuration['cron_notification_mails'] : null;
  451. $minLevel = Logger::DEBUG;
  452. if (!is_array($emails)) {
  453. $emails = array($emails);
  454. }
  455. $subject = "Cron main/cron/import_csv.php ".date('Y-m-d h:i:s');
  456. $from = api_get_setting('emailAdministrator');
  457. if (!empty($emails)) {
  458. foreach ($emails as $email) {
  459. //$stream = new NativeMailerHandler($email, $subject, $from, $minLevel);
  460. //$logger->pushHandler(new BufferHandler($stream, 0, $minLevel));
  461. }
  462. }
  463. $stream = new StreamHandler(api_get_path(SYS_ARCHIVE_PATH).'import_csv.log', $minLevel);
  464. $logger->pushHandler(new BufferHandler($stream, 0, $minLevel));
  465. $logger->pushHandler(new RotatingFileHandler('import_csv', 5, $minLevel));
  466. $import = new ImportCsv($logger);
  467. $dump = false;
  468. if (isset($argv[1]) && $argv[1] = '--dump') {
  469. $dump = true;
  470. }
  471. $import->setDumpValues($dump);
  472. // Do not moves the files to treated
  473. $import->test = true;
  474. $import->run();