Skip to content

Commit 2eb8b69

Browse files
pbailiebmcutler
authored andcommitted
Registration section resync (#12)
* Changes to be committed: new file: sample_bin/registration_section_resync.php WIP: Registration re-sync tool for issue Submitty/Submitty#2960: "Registration Section data not always synced between master DB and course DB" * Changes to be committed: modified: sample_bin/registration_section_resync.php WIP * Changes to be committed: modified: sample_bin/registration_section_resync.php WIP * Changes to be committed: modified: sample_bin/registration_section_resync.php Ready for PR
1 parent e39f774 commit 2eb8b69

File tree

1 file changed

+161
-0
lines changed

1 file changed

+161
-0
lines changed
Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
#!/usr/bin/env php
2+
<?php
3+
4+
/**
5+
* Sysadmin tool to correct out of sync database issue with registration
6+
* sections. Github issue #2690.
7+
*
8+
* Run this script to resync registration sections for all courses registered
9+
* in every term specified on cli.
10+
*
11+
* e.g. ./registration_section_resync.php u18 f18
12+
*
13+
* resync regustration sections for all courses registered in the u18 and f18
14+
* terms.
15+
*
16+
* @author Peter Bailie (systems programmer, RPI dept. of computer science)
17+
*/
18+
19+
error_reporting(E_ERROR);
20+
ini_set('display_errors', 'stderr');
21+
define ('DB_CONFIG_PATH', '/usr/local/submitty/config/database.json');
22+
23+
//Run script
24+
new registration_section_resync();
25+
exit(0);
26+
27+
class registration_section_resync {
28+
29+
private static $db_conn = array('master' => null, 'course' => null);
30+
private static $db_host;
31+
private static $db_user;
32+
private static $db_password;
33+
private static $academic_terms;
34+
35+
public function __construct() {
36+
$this->init();
37+
$this->process();
38+
printf("Re-sync process finished.%s", PHP_EOL);
39+
}
40+
41+
public function __destruct() {
42+
$this->close_db_conn('course');
43+
$this->close_db_conn('master');
44+
}
45+
46+
/** Setup properties and open master DB connection */
47+
private function init() {
48+
//This script should be run as root.
49+
if (posix_getuid() !== 0) {
50+
exit(sprintf("This script must be run as root.%s", PHP_EOL));
51+
}
52+
53+
//This script cannot be run as a webpage.
54+
if (PHP_SAPI !== 'cli') {
55+
exit(sprintf("This script must be run from the command line.%s", PHP_EOL));
56+
}
57+
58+
//Get all academic terms to be processed (as cli arguments).
59+
if ($_SERVER['argc'] < 2) {
60+
exit(sprintf("Please specify at least one academic term code as a command line argument.%s", PHP_EOL));
61+
}
62+
self::$academic_terms = array_splice($_SERVER['argv'], 1);
63+
64+
//Get DB connection config from Submitty
65+
$json_data = file_get_contents(DB_CONFIG_PATH);
66+
if ($json_data === false) {
67+
exit(sprintf("Could not read database configuration at %s%s", DB_CONFIG_PATH, PHP_EOL));
68+
}
69+
70+
$json_data = json_decode($json_data, true);
71+
self::$db_host = $json_data['database_host'];
72+
self::$db_user = $json_data['database_user'];
73+
self::$db_password = $json_data['database_password'];
74+
75+
//Connect to master DB.
76+
self::$db_conn['master'] = pg_connect(sprintf("dbname=submitty host=%s user=%s password=%s sslmode=prefer", self::$db_host, self::$db_user, self::$db_password));
77+
if (pg_connection_status(self::$db_conn['master']) !== PGSQL_CONNECTION_OK) {
78+
exit(sprintf("ERROR: Could not establish connection to Submitty Master DB%sCheck configuration at %s%s", PHP_EOL, DB_CONFIG_PATH, PHP_EOL));
79+
}
80+
}
81+
82+
/** Loop through academic terms and then loop through each course to sync registration sections */
83+
private function process() {
84+
//Loop through academic terms
85+
foreach (self::$academic_terms as $term) {
86+
//Get courses list for $term
87+
$res = pg_query_params(self::$db_conn['master'], "SELECT course FROM courses WHERE semester = $1", array($term));
88+
if ($res === false) {
89+
exit(sprintf("Error reading course list for %s.%s", $term, PHP_EOL));
90+
}
91+
92+
$courses = pg_fetch_all_columns($res, 0);
93+
if (empty($courses)) {
94+
fprintf(STDERR, "No courses registered for %s.%s", $term, PHP_EOL);
95+
continue;
96+
}
97+
98+
//Now loop through all courses
99+
foreach($courses as $course) {
100+
//We need to compare registration sections in both master and course DBs
101+
//Connect to course DB
102+
$db_name = sprintf("submitty_%s_%s", $term, $course);
103+
self::$db_conn['course'] = pg_connect(sprintf("dbname=%s host=%s user=%s password=%s sslmode=prefer", $db_name, self::$db_host, self::$db_user, self::$db_password));
104+
if (pg_connection_status(self::$db_conn['course']) !== PGSQL_CONNECTION_OK) {
105+
fprintf(STDERR, "ERROR: Could not establish connection to Submitty Course DB %s.%sSkipping %s %s.%s", $db_name, PHP_EOL, $term, $course, PHP_EOL);
106+
continue;
107+
}
108+
109+
//First retrieve registration sections in master DB
110+
$res = pg_query_params(self::$db_conn['master'], "SELECT registration_section_id FROM courses_registration_sections WHERE semester=$1 AND course=$2", array($term, $course));
111+
if ($res === false) {
112+
fprintf(STDERR, "Error reading registration sections from master DB: %s %s.%sSkipping %s %s.%s", $term, $course, PHP_EOL, $term, $course, PHP_EOL);
113+
$this->close_db_conn('course');
114+
continue;
115+
}
116+
$master_registration_sections = pg_fetch_all_columns($res, 0);
117+
118+
//Next retrieve registration sections in course DB
119+
$res = pg_query(self::$db_conn['course'], "SELECT sections_registration_id FROM sections_registration");
120+
if ($res === false) {
121+
fprintf(STDERR, "Error reading registration sections from course DB: %s.%sSkipping %s %s.%s", $dbname, PHP_EOL, $term, $course, PHP_EOL);
122+
$this->close_db_conn('course');
123+
continue;
124+
}
125+
$course_registration_sections = pg_fetch_all_columns($res, 0);
126+
127+
//Get the differences of both lists (a registration section in either list, but not the other).
128+
$sync_list = array_diff($course_registration_sections, $master_registration_sections);
129+
$sync_list = array_merge($sync_list, array_diff($master_registration_sections, $course_registration_sections));
130+
if (empty($sync_list)) {
131+
printf("No sync required for %s %s.%s", $term, $course, PHP_EOL);
132+
$this->close_db_conn('course');
133+
continue;
134+
}
135+
136+
//INSERT $sync_list to master DB, ON CONFLICT DO NOTHING (prevents potential PK violations). We're using DB schema trigger to complete resync.
137+
foreach($sync_list as $section) {
138+
$res = pg_query_params(self::$db_conn['master'], "INSERT INTO courses_registration_sections (semester, course, registration_section_id) VALUES ($1, $2, $3) ON CONFLICT ON CONSTRAINT courses_registration_sections_pkey DO UPDATE SET semester=$1, course=$2, registration_section_id=$3", array($term, $course, $section));
139+
if ($res === false) {
140+
fprintf(STDERR, "Error during re-sync procedure: %s %s section %s.%s.This section could not be synced.%s", $term, $course, $section, PHP_EOL, PHP_EOL);
141+
$this->close_db_conn('course');
142+
continue;
143+
}
144+
}
145+
printf("Sync complete for %s %s.%s", $term, $course, PHP_EOL);
146+
$this->close_db_conn('course');
147+
}
148+
}
149+
}
150+
151+
/**
152+
* Close a given DB connection.
153+
*
154+
* @param string DB connection to close.
155+
*/
156+
private function close_db_conn($conn) {
157+
if (pg_connection_status(self::$db_conn[$conn]) === PGSQL_CONNECTION_OK) {
158+
pg_close(self::$db_conn[$conn]);
159+
}
160+
}
161+
}

0 commit comments

Comments
 (0)