server/web: preliminary support for badges

- DB: add tables for badges and badge/user and badge/team associations
- add script that defines 3 RAC-based badges and assigns them
- add images for these badges
- add admin page for creating/editing badges
- show badges on user page
not done:
- figure out how to send badges to client
- display badges somewhere in the GUIs
- export badges in db_dump
- enable badges by default for new projects
This commit is contained in:
David Anderson 2013-12-05 10:14:26 -08:00
parent 8ee575438d
commit 65b5ab5184
12 changed files with 344 additions and 1 deletions

View File

@ -132,3 +132,9 @@ alter table assignment
alter table job_file
add unique jf_md5(md5);
alter table badge_user
add unique (user_id, badge_id);
alter table badge_team
add unique(team_id, badge_id);

View File

@ -690,3 +690,37 @@ create table notify (
opaque integer not null
-- some other ID, e.g. that of the thread, user or PM record
);
create table badge (
id serial primary key,
create_time double not null,
type tinyint not null,
-- 0=user, 1=team
name varchar(255) not null,
-- internal use (not visible to users)
title varchar(255) not null,
-- user-visible, short
description varchar(255) not null,
-- user-visible, possibly longer
image_url varchar(255) not null,
-- location of image
level varchar(255) not null,
-- project-defined
tags varchar(255) not null,
-- project-defined
sql_rule varchar(255) not null
);
create table badge_user (
badge_id integer not null,
user_id integer not null,
create_time double not null,
reassign_time double not null
);
create table badge_team (
badge_id integer not null,
team_id integer not null,
create_time double not null,
reassign_time double not null
);

View File

@ -501,4 +501,72 @@ function latest_avs_app($appid) {
return $r;
}
class BoincBadge {
static function enum($where_clause) {
$db = BoincDb::get();
return $db->enum('badge', 'BoincBadge', $where_clause);
}
static function insert($clause) {
$db = BoincDb::get();
$ret = $db->insert('badge', $clause);
if (!$ret) return 0;
return $db->insert_id();
}
function update($clause) {
$db = BoincDb::get();
return $db->update($this, 'badge', $clause);
}
static function lookup_id($id) {
$db = BoincDb::get();
return $db->lookup_id($id, 'badge', 'BoincBadge');
}
static function lookup($clause) {
$db = BoincDb::get();
return $db->lookup('badge', 'BoincBadge', $clause);
}
}
class BoincBadgeUser {
static function enum($where_clause) {
$db = BoincDb::get();
return $db->enum('badge_user', 'BoincBadgeUser', $where_clause);
}
static function insert($clause) {
$db = BoincDb::get();
$ret = $db->insert('badge_user', $clause);
if (!$ret) return false;
return true;
}
static function lookup($clause) {
$db = BoincDb::get();
return $db->lookup('badge_user', 'BoincBadgeUser', $clause);
}
static function update($clause) {
$db = BoincDb::get();
return $db->update_aux('badge_user', $clause);
}
function delete($clause) {
$db = BoincDb::get();
$db->delete_aux('badge_user', $clause);
}
}
class BoincBadgeTeam {
static function lookup($clause) {
$db = BoincDb::get();
return $db->lookup('badge_team', 'BoincBadgeTeam', $clause);
}
static function insert($clause) {
$db = BoincDb::get();
$ret = $db->insert('badge_team', $clause);
if (!$ret) return false;
return true;
}
function delete($clause) {
$db = BoincDb::get();
$db->delete_aux('badge_team', $clause);
}
}
?>

View File

@ -396,6 +396,17 @@ function community_links($clo, $logged_in_user){
}
}
function show_badges($user) {
$bus = BoincBadgeUser::enum("user_id=$user->id");
if (!$bus) return;
$x = "";
foreach ($bus as $bu) {
$badge = BoincBadge::lookup_id($bu->badge_id);
$x .= "<img title=\"$badge->title\" height=40 src=$badge->image_url> ";
}
row2("Badges", $x);
}
function show_profile_link($user) {
if ($user->has_profile) {
row2(tra("Profile"), "<a href=\"view_profile.php?userid=$user->id\">".tra("View")."</a>");

View File

@ -327,7 +327,7 @@ function table_header() {
$col = func_get_arg($i);
echo "<th ".$col[1].">".$col[0]."</th>\n";
} else {
echo "<th>".func_get_arg($i)."</th>\n";
echo "<th valign=top>".func_get_arg($i)."</th>\n";
}
}
echo "</tr>\n";

114
html/ops/badge_admin.php Normal file
View File

@ -0,0 +1,114 @@
<?php
// This file is part of BOINC.
// http://boinc.berkeley.edu
// Copyright (C) 2013 University of California
//
// BOINC is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License
// as published by the Free Software Foundation,
// either version 3 of the License, or (at your option) any later version.
//
// BOINC is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
// See the GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with BOINC. If not, see <http://www.gnu.org/licenses/>.
// web interface for administering badges
require_once('../inc/util_ops.inc');
function show_form() {
start_table();
table_header(
"ID",
"name",
"type<br>0=user<br>1=team",
"title",
"description",
"image URL",
"level",
"tags"
);
$badges = BoincBadge::enum("");
foreach ($badges as $badge) {
echo "<tr valign=top><form action=badge_admin.php method=POST>";
echo "<input type=hidden name=id value=$badge->id>";
echo "<td>$badge->id</td>\n";
echo "<td><input name=\"name\" value=\"$badge->name\"></td>\n";
echo "<td><input name=\"type\" size=4 value=\"$badge->type\"></td>\n";
echo "<td><input name=\"title\" value=\"$badge->title\"></td>\n";
echo "<td><input name=\"description\" value=\"$badge->description\"></td>\n";
$x = "";
if ($badge->image_url) {
$x = " <img align=right height=64 src=\"$badge->image_url\">";
}
echo "<td><input name=\"image_url\" value=\"$badge->image_url\">$x</td>\n";
echo "<td><input name=\"level\" value=\"$badge->level\"></td>\n";
echo "<td><input name=\"tags\" value=\"$badge->tags\"></td>\n";
echo "<td><input type=submit name=\"update\" value=Update>\n";
echo "</form></tr>\n";
}
echo "<tr><form action=badge_admin.php method=POST>";
echo "<td><br></td>\n";
echo "<td><input name=\"name\"></td>\n";
echo "<td><input name=\"type\" size=4></td>\n";
echo "<td><input name=\"title\"></td>\n";
echo "<td><input name=\"description\"></td>\n";
echo "<td><input name=\"image_url\"></td>\n";
echo "<td><input name=\"level\"></td>\n";
echo "<td><input name=\"tags\"></td>\n";
echo "<td><input type=submit name=\"add_badge\" value=\"Create badge\"></td>\n";
echo "</form></tr>\n";
end_table();
}
function add_badge() {
$name = BoincDb::escape_string(post_str("name"));
$type = post_int("type");
$title = BoincDb::escape_string(post_str("title"));
$description = BoincDb::escape_string(post_str("description"));
$image_url = BoincDb::escape_string(post_str("image_url"));
$level = BoincDb::escape_string(post_str("level"));
$tags = BoincDb::escape_string(post_str("tags"));
$now = time();
$id = BoincBadge::insert("(create_time, name, type, title, description, image_url, level, tags) values ($now, '$name', $type, '$title', '$description', '$image_url', '$level', '$tags')");
if (!$id) {
admin_error_page("Insert failed");
}
}
function update_badge() {
$id = post_int("id");
$badge = BoincBadge::lookup_id($id);
if (!$badge) {
admin_error_page("no such badge");
}
$name = BoincDb::escape_string(post_str("name"));
$type = post_int("type");
$title = BoincDb::escape_string(post_str("title"));
$description = BoincDb::escape_string(post_str("description"));
$image_url = BoincDb::escape_string(post_str("image_url"));
$level = BoincDb::escape_string(post_str("level"));
$tags = BoincDb::escape_string(post_str("tags"));
$retval = $badge->update("name='$name', type=$type, title='$title', description='$description', image_url='$image_url', level='$level', tags='$tags'");
if (!$retval) {
admin_error_page("update failed");
}
}
if (post_str('add_badge', true)) {
add_badge();
} else if (post_str('update', true)) {
update_badge();
}
admin_page_head("Manage badges");
show_form();
admin_page_tail();
?>

105
html/ops/badge_assign.php Executable file
View File

@ -0,0 +1,105 @@
#!/usr/bin/env php
<?php
// This file is part of BOINC.
// http://boinc.berkeley.edu
// Copyright (C) 2013 University of California
//
// BOINC is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License
// as published by the Free Software Foundation,
// either version 3 of the License, or (at your option) any later version.
//
// BOINC is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
// See the GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with BOINC. If not, see <http://www.gnu.org/licenses/>.
// Assign badges based on RAC.
// Customize this to grant other types of badges
require_once("../inc/boinc_db.inc");
define("GOLD_RAC", 100000);
define("SILVER_RAC", 10000);
define("BRONZE_RAC", 1000);
function get_badge($name, $t, $rac, $image_url) {
$b = BoincBadge::lookup("name='$name'");
if ($b) return $b;
$now = time();
$title = "$t badge: average credit > $rac";
$id = BoincBadge::insert("(create_time, name, title, image_url) values ($now, '$name', '$title', 'img/$image_url')");
$b = BoincBadge::lookup_id($id);
if ($b) return $b;
die("can't create badge $name\n");
}
$rac_gold = get_badge("rac_gold", "Gold", GOLD_RAC, "gold.png");
$rac_silver = get_badge("rac_silver", "Silver", SILVER_RAC, "silver.png");
$rac_bronze = get_badge("rac_bronze", "Bronze", BRONZE_RAC, "bronze.png");
function assign_badge($user, $badge) {
$now = time();
$bbu = BoincBadgeUser::lookup("user_id=$user->id and badge_id=$badge->id");
if ($bbu) {
echo "reassigning $badge->name to $user->id\n";
$bbu->update("reassign_time=$now where user_id=$user->id and badge_id=$badge->id");
} else {
echo "assigning $badge->name to $user->id\n";
BoincBadgeUser::insert("(create_time, user_id, badge_id, reassign_time) values ($now, $user->id, $badge->id, $now)");
}
}
function unassign_badges($user, $badges) {
$list = null;
foreach($badges as $badge) {
echo "unassigning $badge->name to $user->id\n";
if ($list) {
$list .= ",$badge->id";
} else {
$list = "$badge->id";
}
}
BoincBadgeUser::delete("user_id=$user->id and badge_id in ($list)");
}
function assign_rac_badge($user) {
global $rac_gold, $rac_silver, $rac_bronze;
if ($user->expavg_credit > GOLD_RAC) {
assign_badge($user, $rac_gold);
unassign_badges($user, array($rac_silver, $rac_bronze));
} else if ($user->expavg_credit > SILVER_RAC) {
assign_badge($user, $rac_silver);
unassign_badges($user, array($rac_bronze, $rac_gold));
} else if ($user->expavg_credit > BRONZE_RAC) {
assign_badge($user, $rac_bronze);
unassign_badges($user, array($rac_gold, $rac_silver));
} else {
unassign_badges($user, array($rac_gold, $rac_silver, $rac_bronze));
}
}
function assign_badges_user($user) {
assign_rac_badge($user);
// ... assign other types of badges
}
function assign_badges() {
$n = 0;
$maxid = BoincUser::max("id");
while ($n <= $maxid) {
$m = $n + 1000;
$users = BoincUser::enum_fields("id, expavg_credit", "id>=$n and id<$m and total_credit>0");
foreach ($users as $user) {
assign_badges_user($user);
}
$n = $m;
}
}
assign_badges();
?>

View File

@ -114,6 +114,7 @@ echo "
<td><b>User management</b>
<ul>
<li><a href=\"profile_screen_form.php\">Screen user profiles </a></li>
<li><a href=\"badge_admin.php\">Badges</a></li>
<li><a href=\"manage_special_users.php\">User privileges</a></li>
<li><a href=".URL_BASE."/manage_project.php>User job submission privileges</a></li>
<li><a href=\"mass_email.php\">Send mass email to a selected set of users</a></li>

BIN
html/user/img/bronze.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

BIN
html/user/img/gold.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

BIN
html/user/img/silver.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

View File

@ -71,6 +71,9 @@ if ($format=="xml"){
} else {
// No data was found, generate new data for the cache and store it
$user = lookup_user_id($id);
if (!$user) {
error_page("No such user $id");
}
BoincForumPrefs::lookup($user);
$user = @get_other_projects($user);
$community_links = get_community_links_object($user);
@ -97,6 +100,7 @@ if ($format=="xml"){
echo "</td><td valign=top>";
start_table();
show_profile_link($user);
show_badges($user);
community_links($community_links, $logged_in_user);
end_table();
echo "</td></tr></table>";