Skip to content

Commit

Permalink
Initial notification settings (Submitty#3133)
Browse files Browse the repository at this point in the history
* first changes

* db

* Notification settings

* Fix merge to all

* tweaks

* all_threads

* tweak db

* Fix query

* tweaks

* Fixes

* Migration tweaks for fix on rollback

* Fix dupes

* Fix user unit

* type

* Fix array

* Fix announcements

* tweaks

* move sql

* remove log

* remove imports..

* reword

* reword more

* typos

* spelling

* rephrase

* more spelling challenges

* fix spacing

* fix if spacing
  • Loading branch information
andrewaikens87 authored and bmcutler committed Dec 22, 2018
1 parent cfb7dfd commit 5bd0951
Show file tree
Hide file tree
Showing 19 changed files with 345 additions and 48 deletions.
2 changes: 1 addition & 1 deletion grading/execute.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1034,7 +1034,7 @@ void cin_reader(std::mutex* lock, std::queue<std::string>* input_queue, bool* CH

if (ret > 0){
std::getline (std::cin,my_string);
std::cout << "Cin recieved: " << my_string << std::endl;
std::cout << "Cin received: " << my_string << std::endl;

lock->lock();
input_queue->push(my_string);
Expand Down
2 changes: 1 addition & 1 deletion grading/json.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -7786,7 +7786,7 @@ class basic_json
@param[in] type the @ref number_float_t in use
@param[in,out] endptr recieves a pointer to the first character after
@param[in,out] endptr receives a pointer to the first character after
the number
@return the floating point number
Expand Down
4 changes: 2 additions & 2 deletions grading/python/submitty_router.py
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@ def listen_to_sockets():
sys.exit(1)

#TODO: May have to the max recvfrom size.
#The recvfrom call will raise a OSError if there is nothing to recieve.
#The recvfrom call will raise a OSError if there is nothing to receive.
message, snd = connection.recvfrom(4096)
sender = SWITCHBOARD[port]['sender'].replace("_Actual", "")

Expand All @@ -227,7 +227,7 @@ def listen_to_sockets():
SWITCHBOARD[port]['connection'] = None
continue

log('Recieved message {!r} from {} on port {}'.format(message,sender,port))
log('Received message {!r} from {} on port {}'.format(message,sender,port))

#if we did not error:
connect_outgoing_socket(port)
Expand Down
15 changes: 15 additions & 0 deletions migration/data/course_tables.sql
Original file line number Diff line number Diff line change
Expand Up @@ -497,6 +497,15 @@ CREATE TABLE regrade_requests (
);


CREATE TABLE notification_settings (
user_id character varying NOT NULL,
merge_threads BOOLEAN DEFAULT FALSE NOT NULL,
all_new_threads BOOLEAN DEFAULT FALSE NOT NULL,
all_new_posts BOOLEAN DEFAULT FALSE NOT NULL,
all_modifications_forum BOOLEAN DEFAULT FALSE NOT NULL,
reply_in_post_thread BOOLEAN DEFAULT FALSE NOT NULL
);

--
-- Name: regrade_discussion; Type: TABLE; Schema: public; Owner: -
--
Expand Down Expand Up @@ -765,6 +774,12 @@ ALTER TABLE ONLY sessions
ALTER TABLE ONLY users
ADD CONSTRAINT users_pkey PRIMARY KEY (user_id);

ALTER TABLE ONLY notification_settings
ADD CONSTRAINT notification_settings_pkey PRIMARY KEY (user_id);

ALTER TABLE ONLY notification_settings
ADD CONSTRAINT notification_settings_fkey FOREIGN KEY (user_id) REFERENCES users(user_id) ON UPDATE CASCADE;

--
-- Name: gradeable_teams_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
def up(config, conn, semester, course):
with conn.cursor() as cursor:
cursor.execute('ALTER TABLE electronic_gradeable DROP COLUMN eg_student_download')
cursor.execute('ALTER TABLE electronic_gradeable DROP COLUMN eg_student_any_version')
cursor.execute('ALTER TABLE electronic_gradeable DROP COLUMN IF EXISTS eg_student_download')
cursor.execute('ALTER TABLE electronic_gradeable DROP COLUMN IF EXISTS eg_student_any_version')


def down(config, conn, semester, course):
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
def up(config, conn, semester, course):
with conn.cursor() as cursor:
cursor.execute("""CREATE TABLE IF NOT EXISTS notification_settings (
user_id character varying NOT NULL,
merge_threads BOOLEAN DEFAULT FALSE NOT NULL,
all_new_threads BOOLEAN DEFAULT FALSE NOT NULL,
all_new_posts BOOLEAN DEFAULT FALSE NOT NULL,
all_modifications_forum BOOLEAN DEFAULT FALSE NOT NULL,
reply_in_post_thread BOOLEAN DEFAULT FALSE NOT NULL);""")
cursor.execute("ALTER TABLE ONLY notification_settings DROP CONSTRAINT IF EXISTS notification_settings_pkey;")
cursor.execute("ALTER TABLE ONLY notification_settings DROP CONSTRAINT IF EXISTS notification_settings_fkey;")
cursor.execute("ALTER TABLE ONLY notification_settings ADD CONSTRAINT notification_settings_pkey PRIMARY KEY (user_id);")
cursor.execute("ALTER TABLE ONLY notification_settings ADD CONSTRAINT notification_settings_fkey FOREIGN KEY (user_id) REFERENCES users(user_id) ON UPDATE CASCADE;")
cursor.execute("INSERT INTO notification_settings SELECT user_id from users ON CONFLICT DO NOTHING")

def down(config, conn, semester, course):
pass
6 changes: 3 additions & 3 deletions sbin/shipper_utils/systemctl_wrapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,14 +47,14 @@ def print_status_message(status_code, mode, daemon, machine):
elif status_code == 2:
print("{0}Failure performing the {1} operation".format(prefix, mode))
elif status_code == 3:
print("{0}Recieved an argument error. This could be an issue with this script.".format(prefix))
print("{0}Received an argument error. This could be an issue with this script.".format(prefix))
else:
print("{0}Recieved unknown status code {1} when attempting to {2} the \
print("{0}Received unknown status code {1} when attempting to {2} the \
{3} daemon".format(prefix, status_code, mode, daemon))

# A wrapper for perform_systemctl_command_on_worker that iterates over all workers.
def perform_systemctl_command_on_all_workers(daemon, mode):
# Right now, this script returns the greatesr (worst) status it recieves from a worker.
# Right now, this script returns the greatesr (worst) status it receives from a worker.
greatest_status = 0

for target in WORKERS.keys():
Expand Down
3 changes: 2 additions & 1 deletion site/app/controllers/NavigationController.php
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,8 @@ private function notificationsHandler() {
return $this->core->getOutput()->renderTwigOutput("Notifications.twig", [
'course' => $currentCourse,
'show_all' => $show_all,
'notifications' => $notifications
'notifications' => $notifications,
'notification_saves' => $this->core->getUser()->getNotificationSettings()
]);
}
}
Expand Down
54 changes: 54 additions & 0 deletions site/app/controllers/NotificationSettings.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<?php

namespace app\controllers;

use app\libraries\Core;
use app\models\Notification;
use app\controllers\AbstractController;
use app\libraries\Output;

/**
* Class NotificationSettings
*
*/
class NotificationSettings extends AbstractController {

const NOTIFICATION_SELECTIONS = ['merge_threads', 'all_new_threads', 'all_new_posts', 'all_modifications_forum', 'reply_in_post_thread'];
const NUM = 5;

public function __construct(Core $core) {
parent::__construct($core);
}

public function run() {
switch ($_REQUEST['page']) {
case 'alter_notifcation_settings':
$this->changeSettings();
break;
}
}

public function changeSettings() {
//Change settings for the current user...
$new_settings = $_POST;
$result = ['error' => 'Notification settings could not be saved. Please try again.'];
if($this->validateNotificationSettings(array_keys($new_settings))) {
$values_not_sent = array_diff(self::NOTIFICATION_SELECTIONS, array_keys($new_settings));
foreach(array_values($values_not_sent) as $value) {
$new_settings[$value] = 'false';
}
$this->core->getQueries()->updateNotificationSettings($new_settings);
$result = ['success' => 'Notification settings have been saved.'];
}
$this->core->getOutput()->renderJson($result);
return $this->core->getOutput()->getOutput();
}

private function validateNotificationSettings($columns) {
if(count($columns) <= self::NUM && count(array_intersect($columns, self::NOTIFICATION_SELECTIONS)) == count($columns)) {
return true;
}
return false;
}

}
6 changes: 2 additions & 4 deletions site/app/controllers/forum/ForumController.php
Original file line number Diff line number Diff line change
Expand Up @@ -352,11 +352,9 @@ public function publishThread(){
}

}
if($announcment){
$notification = new Notification($this->core, array('component' => 'forum', 'type' => 'new_announcement', 'thread_id' => $id, 'thread_title' => $title));

$this->core->getQueries()->pushNotification($notification);
}
$notification = new Notification($this->core, array('component' => 'forum', 'type' => $announcment ? 'new_announcement' : 'new_thread', 'thread_id' => $id, 'thread_title' => $title));
$this->core->getQueries()->pushNotification($notification);

if($email_announcement) {
$this->sendEmailAnnouncement($title, $thread_post_content, $post_id);
Expand Down
87 changes: 73 additions & 14 deletions site/app/libraries/database/DatabaseQueries.php
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,32 @@ public function updateResolveState($thread_id, $state) {
return false;
}

public function updateNotificationSettings($results) {
$values = implode(', ', array_fill(0, count($results)+1, '?'));
$keys = implode(', ', array_keys($results));
$updates = '';

foreach($results as $key => $value) {
if($value != 'false') {
$results[$key] = 'true';
}
$this->core->getUser()->updateUserNotificationSettings($key, $results[$key] == 'true' ? true : false);
$updates .= $key . ' = ?,';
}

$updates = substr($updates, 0, -1);
$test = array_merge(array_merge(array($this->core->getUser()->getId()), array_values($results)), array_values($results));
$this->course_db->query("INSERT INTO notification_settings (user_id, $keys)
VALUES
(
$values
)
ON CONFLICT (user_id)
DO
UPDATE
SET $updates", $test);
}

public function getAuthorOfThread($thread_id) {
$this->course_db->query("SELECT created_by from threads where id = ?", array($thread_id));
return $this->course_db->rows()[0]['created_by'];
Expand Down Expand Up @@ -2685,25 +2711,58 @@ public function pushNotification($notification){
$params[] = $notification->getNotifyContent();
$params[] = $notification->getNotifySource();

if(empty($notification->getNotifyTarget())) {
// Notify all users
$target_users_query = "SELECT user_id FROM users";
} else {
// To a specific user
$params[] = $notification->getNotifyTarget();
$target_users_query = "SELECT ?::text as user_id";
$type = $notification->getType();
$target_users_query = "SELECT user_id FROM users";
$ignore_self_query = "";
$not_send_users = array();
$announcement = $type === 'new_announcement' || $type === 'updated_announcement';

if(!empty($notification->getNotifyTarget())) {
//Notify specific user
$not_send_users[] = $notification->getNotifyTarget();
if($params[3] !== $not_send_users[0]) {
$this->course_db->query("INSERT INTO notifications(component, metadata, content, created_at, from_user_id, to_user_id)
VALUES (?, ?, ?, current_timestamp, ?, ?)",
array_merge($params, $not_send_users));
}
}

if($notification->getNotifyNotToSource()){
$ignore_self_query = "WHERE user_id <> ?";
$params[] = $notification->getNotifySource();
}
else {
$ignore_self_query = "";
}
$not_send_users[] = $notification->getNotifySource();
}

$restrict = count($not_send_users);
if($restrict > 0) {
$ignore_self_query = "WHERE user_id NOT IN (" . implode(',', array_fill(0, $restrict, '?')) . ')';
}

$column = '';
if($type === 'reply') {
$post_thread_id = json_decode($params[1], true)[0]['thread_id'];
$params[] = $post_thread_id;
$target_users_query = "SELECT n.user_id from notification_settings n, posts p where p.thread_id = ? and p.author_user_id = n.user_id and n.reply_in_post_thread = 'true' ";
$target_users_query .= "UNION SELECT user_id from notification_settings where all_new_posts = 'true'";
} else if(!$announcement) {
switch ($type) {
case 'new_thread':
$column = 'all_new_threads';
break;
case 'merge_thread':
$column = 'merge_threads';
break;
case 'edited':
case 'deleted':
case 'undeleted':
$column = 'all_modifications_forum';
break;
}
$target_users_query = "SELECT user_id FROM notification_settings where {$column} = 'true'";
}

//Notify users based on settings
$this->course_db->query("INSERT INTO notifications(component, metadata, content, created_at, from_user_id, to_user_id)
SELECT ?, ?, ?, current_timestamp, ?, user_id as to_user_id FROM ({$target_users_query}) as u {$ignore_self_query}",
$params);
array_merge($params, $not_send_users));
}

/**
Expand Down
22 changes: 9 additions & 13 deletions site/app/libraries/database/PostgresqlDatabaseQueries.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,11 @@ class PostgresqlDatabaseQueries extends DatabaseQueries{

public function getUserById($user_id) {
$this->course_db->query("
SELECT u.*, sr.grading_registration_sections
SELECT u.*, ns.merge_threads, ns.all_new_threads,
ns.all_new_posts, ns.all_modifications_forum,
ns.reply_in_post_thread, sr.grading_registration_sections
FROM users u
LEFT JOIN notification_settings as ns ON u.user_id = ns.user_id
LEFT JOIN (
SELECT array_agg(sections_registration_id) as grading_registration_sections, user_id
FROM grading_registration
Expand Down Expand Up @@ -628,18 +631,10 @@ public function getAverageAutogradedScores($g_id, $section_key, $is_team) {
$this->course_db->query("
SELECT round((AVG(score)),2) AS avg_score, round(stddev_pop(score), 2) AS std_dev, 0 AS max, COUNT(*) FROM(
SELECT * FROM (
SELECT (egd.autograding_non_hidden_non_extra_credit + egd.autograding_non_hidden_extra_credit + egd.autograding_hidden_non_extra_credit + egd.autograding_hidden_extra_credit) AS score
FROM electronic_gradeable_data AS egd
INNER JOIN (
SELECT {$user_or_team_id}, {$section_key} FROM {$users_or_teams}
) AS {$u_or_t}
ON {$u_or_t}.{$user_or_team_id} = egd.{$user_or_team_id}
INNER JOIN (
SELECT g_id, {$user_or_team_id}, active_version FROM electronic_gradeable_version AS egv
WHERE active_version > 0
) AS egv
ON egd.g_id=egv.g_id AND egd.{$user_or_team_id}=egv.{$user_or_team_id} AND egd.g_version=egv.active_version
WHERE egd.g_id=? AND {$u_or_t}.{$section_key} IS NOT NULL
SELECT (egv.autograding_non_hidden_non_extra_credit + egv.autograding_non_hidden_extra_credit + egv.autograding_hidden_non_extra_credit + egv.autograding_hidden_extra_credit) AS score
FROM electronic_gradeable_data AS egv
INNER JOIN {$users_or_teams} AS {$u_or_t} ON {$u_or_t}.{$user_or_team_id} = egv.{$user_or_team_id}, electronic_gradeable_version AS egd
WHERE egv.g_id=? AND {$u_or_t}.{$section_key} IS NOT NULL AND egv.g_version=egd.active_version AND active_version>0 AND egd.{$user_or_team_id}=egv.{$user_or_team_id}
)g
) as individual;
", array($g_id));
Expand Down Expand Up @@ -1854,3 +1849,4 @@ public function getGradeableConfigs($ids, $sort_keys = ['id']) {
$gradeable_constructor);
}
}

Loading

0 comments on commit 5bd0951

Please sign in to comment.