PRS Presentation Building Blocks Conference 2006

Download Report

Transcript PRS Presentation Building Blocks Conference 2006

Clickers on Campus:
Delivering a Time-saving
Integration
Kevin Borgeson
Developer
University of Nebraska at Lincoln
© Blackboard, Inc. All rights reserved.
Clicker System
I will be talking specifically about the Interwrite
PRS™ system from GTCO Calcomp
» Provides immediate feedback to instructors
» Enhances class interactivity, engages students
» 3 Main components to system
»
Classroom receiver
» Wireless IR transmitter w/unique ID (this is the “clicker”)
» Java based client software
»
2
Clicker System
»
Clickers, receivers, and client software in the classroom
Responses captured and
transferred to classroom
computer via serial port
Roster
Lessons
Exports
Settings
Client
software
manages
sessions and
data
All data stored as
non-encrypted
comma-delimited
text files
Gradebook
ResponseMap
Session
3
Clicker Demo:
Building Blocks Survey
4
Clicker System
»
Creating a New PRS Class - Information
5
Clicker System
»
Creating a New PRS Class - Roster
This is the full path to this courses
roster file. Copying over this file with a
correctly formatted .csv file will allow
you to update the roster information.
Roster information links a student to their clicker so that
responses can be gathered. This information must be manually
entered and maintained if there is no way to get updated roster
files for importing.
6
Clicker System Issues
Client software lacks roster auto-updating feature
» Open nature of text files stored locally may
expose sensitive data (e.g. student IDs,
gradebook information, etc)
» Need a simple way for students to register their
clickers, and for instructors to retrieve PRS
formatted roster files for their classes
»
7
Solution
»
Utilize Blackboard as a place for students to register their
clickers
»
»
Already familiar with the Blackboard environment
Use students context information to simplify registration process
Hook into the Blackboard course space to provide a
logical place for instructors to get their roster files, and to
request automatic roster updates
» Store information in a MySQL database for easier retrieval
» Use Active Directory authentication to map a drive to a
server folder for each instructor, then set that as the
default location for the PRS client software
»
»
»
Keeps their data more confidential and secure
Allows us to overwrite the roster file on the server, thus updating
registered clicker information
8
MySQL Database Set-Up
»
Clickers Table
Fields:
id – int autoincrement
prsid – varchar()
studentid – varchar()
»
»
PRS Courses Table
Fields:
id – int autoincrement
courseid – varchar()
»
9
My.UNL Clicker Registration
»
Blackboard registration page
»
Added link to existing Academic Resources tab
Tool Tabs assigned to
the Student role
Link to the clicker registration page
<a href="clicker_reg.jsp" class="sidelnks
style1">register my clicker</a>
10
My.UNL Clicker Registration
If user
already has
a registered
clicker,
clicker ID
shows here
User is required to
enter clicker ID
twice to verify
entered ID is
correct
11
My.UNL Clicker Registration
»
Check For Registered ID
…
// get Users ID from context
User user = bbContext.getUser();
String batchUid = user.getBatchUid();
// if user already has clicker code in db, set flag
String query = "SELECT prsid FROM prsids WHERE
studentid=?";
PreparedStatement pstmt =
conn.prepareStatement(query);
pstmt.setString(1, batchUid);
ResultSet results = pstmt.executeQuery();
if (results.next()) { // record exists
clickerCode = results.getString("prsid");
userFound = "true";
}
…
<input type="text" name="clicker_code_1"
value='<%=clickerCode%>' size="9" maxlength="9"/>
…
»
Verification Javascript
function verifyClickerCode() {
if (document.regForm.clicker_code_1.value ==
document.regForm.clicker_code_2.value) {
// if both codes match, go ahead and submit the form
document.regForm.submit();
} else {
/*switchDiv sets the visibility of the DIV element
containing the error message for mis-matching clicker
codes to visible, thus letting the user know they did
not enter the same code twice*/
switchDiv("nomatch");
// clear the form values for the next attempt
document.regForm.clicker_code_1.value = "";
document.regForm.clicker_code_2.value = "";
}
}
12
My.UNL Clicker Registration
»
Receipt Page
This String set by
variable strHeader
This String set by variable
strMessage
13
…
String clickerCode = request.getParameter("clicker_code_1");
if ((clickerCode.length() > 0) && (clickerCode.length() < 10)) { // make sure length is less than 10 digits
for (int i=0;i<clickerCode.length();i++) {
String sub = clickerCode.substring(i,i+1);
if (sub.matches("\\D+")) { // check to make sure provided code only contains numbers
strHeader = "Problem registering clicker code:";
strMessage = "The code you provided included incorrect characters. A correct clicker code should only contain numbers "
+ "with no letters or special characters. Please go back and try again.";
isIllegalCharacter = true;
}
}
user_found passed as a hidden
…
User user = bbContext.getUser();
String userFound = request.getParameter("user_found");
if (userFound.equals("false")) {
strHeader = "Thank You, " + user.getGivenName() + ".";
strMessage = "Your clicker code has been registered as " + clickerCode;
// do SQL update if user already has a record, insert if no code on file
if (userFound.equals("true")) {
String query = "UPDATE prsids SET prsid=? WHERE studentid=?";
PreparedStatement pstmt = conn.prepareStatement(query);
pstmt.setString(1, clickerCode);
pstmt.setString(2, user.getBatchUid());
pstmt.execute();
} else if (userFound.equals("false")) {
String query = "INSERT INTO prsids (prsid,studentid) VALUES (?,?)";
PreparedStatement pstmt = conn.prepareStatement(query);
pstmt.setString(1, clickerCode);
pstmt.setString(2, user.getBatchUid());
pstmt.execute();
pstmt.close();
}
…
Validate users input
variable in POSTed form, lets us
know if a user was found already
(don’t have to make another
database call)
Update users clicker ID record if
they already have one registered
Insert new record in clickers
database if none was found
14
Bookstore Clicker Registration
»
»
»
»
Custom built Student ID proxy
reader connected to a PRS
receiver
Placed in Bookstore, students
can register their clickers
immediately after purchase
Runs on Linux distribution
Once a student presses a
button on their clicker, puts their
student ID against the proxy
reader, and confirms their
information is correct, a record
is added to the clickers
database
15
PRS Tools Building Block
Course Tool for Instructors
» Instructors can request PRS roster files to
automatically be generated and updated in the
PRS client software for this course
» View status of students clicker registrations
» Export PRS formatted roster file
»
16
PRS Tools Building Block
»
Configuration Page
Data stored in “prs.prop” in Building Block’s Config
directory:
…
FileOutputStream fileConfiguration = null;
…
File fileConfigDir = PlugInUtil.getConfigDirectory("unl", "prstools");
Properties pConfiguration = new Properties();
// Set Properties here
…
fileConfiguration = new FileOutputStream((fileConfigDir.getPath()
+ File.separator + "prs.prop"));
pConfiguration.store(fileConfiguration, "PRS Tools Configuration");
…
17
PRS Tools Building Block
»
BB-Manifest – Tool Definition
…
<application type="course" handle="prstools" use-ssl="false" name="PRS Course
Tools" can-allow-guest="false"
small-icon="" large-icon="">
<description lang="en_US">Course tools for PRS system.</description>
<links>
<link>
<type value="course_tool"/>
<name value="PRS Tools"/>
<url value="prs/tools_index.jsp" />
<description value="Tools for interacting with the PRS Interwrite system." />
</link>
</links>
</application>
…
18
PRS Tools Building Block
…
<bbData:context id="ctx" entitlement="course.VIEW">
<%Course crse = (Course) ctx.getCourse();
Course.ServiceLevel csl = crse.getServiceLevelType();
if (csl.equals(Course.ServiceLevel.COMMUNITY)) { %>
<!-- Show error page if this tool is accessed from a Bb Organization -->
<bbUI:receipt type="FAIL" title="Course Tool Only">
<p>Access to this tool is only available within a course. You are currently in an organization.</p>
</bbUI:receipt>
<% } else { %>
…
19
PRS Tools Building Block
»
BB-Manifest – Permissions
…
<permissions>
<permission type="attribute" name="user.authinfo" actions="get" />
<permission type="attribute" name="user.personalinfo" actions="get" />
<permission type="runtime" name="db.connection.bb_bb60" />
<permission type="socket" name="*" actions="connect,resolve" />
</permissions>
…
20
PRS Tools Building Block
»
Main Page - Components
<bbData:context id="ctx“ entitlement="course.VIEW">
…
Course crse = (Course) ctx.getCourse();
…
<bbUI:docTemplate>
<bbUI:coursePage courseId='<%=crse.getId()%>'>
<bbUI:breadcrumbBar environment="CTRL_PANEL"
handle="control_panel">
<bbUI:breadcrumb>PRS Tools</bbUI:breadcrumb>
</bbUI:breadcrumbBar>
…
Status bar and message customized based on if
there is a record for this course in the Courses
database
tools/export_registered.jsp?course_id="+crse.getId().toExternalString()
tools/view_registered.jsp?course_id="+crse.getId().toExternalString()
21
PRS Tools Building Block
»
Changing PRS Roster
Auto-Update Status
»
»
»
CourseID is persisted to
Courses database
Message and status bar text
changed
Name of roster file is
displayed for instructor (file
name is CourseID+.csv)
22
PRS Tools Building Block
»
Exporting Building Block
…
// force client to recognize this as a file download
response.setContentType("application/octet-stream");
response.setHeader("ContentDisposition","attachment;filename="+course.getCours
eId()+".csv");
…
23
PRS Tools Building Block
»
PRS Roster File Format (BBDEVCONF.csv)
»
»
»
Clicker ID must be unique, otherwise client software will make you
choose one of the records and delete all others when you try to
import
Student ID field must be filled or you cannot grade your session, I
just put the Blackboard username there (I would not put a students
real ID there in case this text file got into the wrong hands)
Net ID needs to be their Blackboard username, needed when
trying to import grades into Blackboard
// Roster (Generated From Bb), Date&Time: 6/20/2006 1:56 PM, Class Name: Bb Developers Conference
TransId, Group Id, Last Name, First Name, Middle Initial, Student ID, Nickname, Net ID
416669,0,Test,PRS1,-,s-prs1,-,s-prs1
416664,0,Test,PRS2,-,s-prs2,-,s-prs2
416665,0,Test,PRS3,-,s-prs3,-,s-prs3
416663,0,Test,PRS4,-,s-prs4,-,s-prs4
416668,0,Test,PRS5,-,s-prs5,-,s-prs5
24
PRS Tools Building Block
…
out.println("// Roster (Generated From Bb), Date&Time: " + dateTime + ", Class Name: " + course.getTitle(););
out.println("TransId, Group Id, Last Name, First Name, Middle Initial, Student ID, Nickname, Net ID");
Connection blackboardDefaultConnection = null;
Pull a Blackboard
BbList memberships = null;
database connection from
connection pool (needed
try {
to make heavyweight
method call)
blackboardDefaultConnection = ConnectionManager.getDefaultConnection();
// load all CourseMembership objects with STUDENT role
// NOTE: must use method loadByCourseIdAndRole(Id,CourseMembership.Role,java.sql.Connection,boolean) to
//
specify you want the User information attached, otherwise null values will be returned with getUser()
memberships =
cmLoader.loadByCourseIdAndRole(courseId,CourseMembership.Role.STUDENT,blackboardDefaultConnection,true);
CourseMembershipComparator cmc = new CourseMembershipComparator();
Collections.sort(memberships, cmc);
} catch (Exception e) {
throw e;
} finally {
ConnectionManager.releaseDefaultConnection(blackboardDefaultConnection);
}
…
Sort students by Last
name
Always release your
connection back to the
pool!
25
PRS Tools Building Block
…
String query = "SELECT prsid,studentid FROM prsids WHERE studentid=?";
PreparedStatement pstmt = conn.prepareStatement(query);
ResultSet results = null;
if (memberships.size() > 0) { // make sure there are students in the course
Iterator iter = memberships.iterator();
while (iter.hasNext()) {
CourseMembership cm = (CourseMembership) iter.next();
User user = cm.getUser();
pstmt.setString(1,user.getBatchUid());
results = pstmt.executeQuery();
if (results.first()) { // found transmitter ID for student, output to file
transId = results.getString("prsid");
lastName = user.getFamilyName();
firstName = user.getGivenName();
if (user.getMiddleName().length() > 1) {
middleInitial = user.getMiddleName().substring(0,1).toUpperCase();
}
if (user.getMiddleName().length() == 1) {
middleInitial = user.getMiddleName().toUpperCase();
}
Create a
PreparedStatement
object connected to
the clickers database
As we iterate through each student
enrolled in this course, bind their
BatchUid to the already created
PreparedStatement and execute a
search for this user in the clickers
database
studentId = user.getUserName();
netId = user.getUserName();
// print record to roster file
out.println(transId+","+groupId+","+lastName+","+firstName+","+middleInitial+","+studentId+","+nickname+","+netId);
// reset middle initial for next record
middleInitial="-";
}
}
…
26
PRS Tools Building Block
»
Viewing Clicker Registrations
…
<bbUI:docTemplate>
<bbUI:coursePage courseId='<%=course.getId()%>'>
<bbUI:breadcrumbBar environment="CTRL_PANEL"
handle="unl-prstools-nav-1">
<bbUI:breadcrumb>Clicker
Registrations</bbUI:breadcrumb>
</bbUI:breadcrumbBar>
…
…
<bbUI:actionBar>
<bbUI:actionItem title="<b>Registered</b>"
href='<%=viewRegisteredLoc%>'
imgUrl="/images/ci/icons/check.gif"/>
<bbUI:actionItem title="Not Registered"
href='<%=viewNotRegisteredLoc%>'
imgUrl="/images/ci/icons/x.gif"/>
</bbUI:actionBar>
…
…
<bbUI:list collection="<%=registered%>" collectionLabel="Registered Users"
objectId="m" className="UsersClickerTO" resultsPerPage="20">
<bbUI:listElement label="Clicker Id" name="clicker">
<%=m.getClickerId()%>
</bbUI:listElement>
<bbUI:listElement label="User Name" name="username">
<%=m.getUser().getUserName()%>
</bbUI:listElement>
<bbUI:listElement label="First Name" name="firstname">
<%=m.getUser().getGivenName()%>
</bbUI:listElement>
<bbUI:listElement label="Last Name" name="lastname">
<%=m.getUser().getFamilyName()%>
</bbUI:listElement>
</bbUI:list>
…
27
Generating Rosters For PRS Flagged Courses
Standalone Java process generates PRS Roster
files for all records found in the courses database
(instructors who want their rosters automatically
updated in the client software)
» Roster files are put in a folder with the instructors
user login as the name
» Shell script executes the Java process, scheduled
to run by a cron job
»
28
Generating Rosters For PRS Flagged Courses
»
Shell Script
#!/bin/sh
This file initializes services managed by class
blackboard.platform.BbServiceManager (which we need to
get access to loaders and persisters). I just copied the file
service-config-snapshot-jdbc.properties and changed the
parameter
‘blackboard.service.log.param.logdef.default.logFile’ to
point to a new log file.
JAVA_HOME=/usr/j2sdk1.4.2_02
JAVA_EXEC=$JAVA_HOME/bin/java
BBDIR=/usr/local/blackboard
BBLIB=$BBDIR/systemlib
PRS_LIB=$BBDIR/data/prs/lib
PRS_CLASSES=$BBDIR/data/prs/classes
CONFIG_FILE=$BBDIR/data/prs/prop/prs.properties
VI=<put your Virtual Installation name here>
FTP_BASE_DIR=/usr/local/blackboard/data/prs/ftp
CP=.:$PRS_CLASSES:$PRS_LIB/mysql-connector-java-3.1.7-bin.jar:$PRS_LIB/jargp.jar:$BBLIB/bbplatform.jar:$BBLIB/xercesImpl.jar:$BBLIB/xmlapis.jar:$BBLIB/jdbc/original.classes12.zip:$BBLIB/gnu-regexp-1.0.8.jar
cd ./prs
"$JAVA_EXEC" -cp "$CP" PrsRoster -V "$VI" -S "$CONFIG_FILE" -D "$FTP_BASE_DIR"
exit 0
29
Generating Rosters For PRS Flagged Courses
…
try {
BbServiceManager.init( config );
} catch (Exception e) {
System.out.println("Error initializing BbServiceManager.");
throw e;
}
This is the config file passed in the command line
/usr/local/blackboard/data/prs/prop/prs.properties
VirtualInstallationManager vMgr = null;
VirtualHost vHost = null;
try {
cMgr = (ContextManagerServerImpl)BbServiceManager.lookupService( ContextManager.class );
vMgr = (VirtualInstallationManager)BbServiceManager.lookupService( VirtualInstallationManager.class );
vHost = vMgr.getVirtualHost( vi );
cMgr.setContext(vHost);
} catch (Exception e) {
Once we get the
System.out.println("Error finding virtual host name");
DbPersistenceManager loaded,
throw e;
}
we can use Blackboard’s loaders
and persisters
try {
bbPm = BbServiceManager.getPersistenceService().getDbPersistenceManager();
crseLoader = (CourseDbLoader) bbPm.getLoader( CourseDbLoader.TYPE );
cmLoader = (CourseMembershipDbLoader) bbPm.getLoader( CourseMembershipDbLoader.TYPE );
} catch (Exception e) {
System.out.println("Error initializing DbPersistenceManager");
throw e;
}
…
30
Generating Rosters For PRS Flagged Courses
…
Course crse = null;
String query = "SELECT courseid FROM prscourse";
Statement stmt = prsCoursesConn.createStatement();
ResultSet results = stmt.executeQuery(query);
// loop through each course found and generate a PRS Roster
while (results.next()) {
try {
crse = (Course) crseLoader.loadByCourseId(results.getString("courseid"));
} catch (KeyNotFoundException e) {
// if this course is not found loop to the next course
continue;
}
BbList instructorMemberships = cmLoader.loadByCourseIdAndRole(crse.getId(),CourseMembership.Role.INSTRUCTOR,blackboardDefaultConn,true);
BbList studentMemberships = cmLoader.loadByCourseIdAndRole(crse.getId(),CourseMembership.Role.STUDENT,blackboardDefaultConn,true);
String roster = generatePrsCourseRoster(studentMemberships, results.getString("courseid"));
This subroutine will return a PRS
formatted file stored in a String
object
Iterator iter = instructorMemberships.iterator();
while(iter.hasNext()) {
CourseMembership cm = (CourseMembership) iter.next();
String instructorName = cm.getUser().getUserName();
String rosterName = results.getString("courseid") + ".csv";
(new File(pr.m_fdFlag+"/"+instructorName+"/PRS/Roster")).mkdirs();
try {
BufferedWriter out = new BufferedWriter(new FileWriter(pr.m_fdFlag+"/"+instructorName+"/PRS/Roster/"+rosterName));
out.write(roster);
out.close();
} catch (IOException e) {
System.out.println("Error writing roster file");
throw e;
}
Write a roster file to
each instructors folder
}
…
31
Transfer Updated PRS Roster Files
»
»
»
»
»
»
Transfer generated folder structure to a Windows Server
Require Active Directory logins on classroom computers
Map a drive based on their Active Directory login name to their folder on the server
Set the default user profile on the classroom computer PRS client folder to this mapped
drive
Roster files updated on the server will now be reflected in the classroom via mapped
drive
Only have permissions to see what is in their folder, data is partitioned, cant see others
data
Login
PRS
Roster
Class1.csv
Class2.csv
32
PRS Roster files
updated via FTP
Overview Diagram
Blackboard Server
PRS Roster
File
Generator
Login
PRS
PRS Courses
Course Tool
Roster
Windows Server
Class1.csv
Class2.csv
Clicker
Registration
Clickers
Login
PRS
Export PRS Roster
manually – save file
to local computer
Bookstore
Clicker
Registration
Roster
Class1.csv
Mapped drive gives
instructor access only
to folder with their
login name
Classroom Computer –
PRS client software
default folder set to
mapped drive
Log in to Active
Directory account –
drive is mapped to
your login on Windows
server
Class2.csv
Active
Directory
33
Conclusion
26 PRS Flagged Courses
» 22 PRS equipped classrooms on campus
» Students own their clicker for the duration of their
academic career (assuming they don’t lose or
destroy it)
» Currently $30 for a clicker
»
34
Q&A
Questions??
Contact Information: [email protected]
35