Skip to content
This repository has been archived by the owner on Oct 29, 2019. It is now read-only.

Commit

Permalink
WL-3230
Browse files Browse the repository at this point in the history
  • Loading branch information
buckett committed Apr 3, 2014
1 parent a42bc52 commit 607bd92
Show file tree
Hide file tree
Showing 8 changed files with 425 additions and 1 deletion.
2 changes: 1 addition & 1 deletion dav
Empty file.
14 changes: 14 additions & 0 deletions extras/admin-reporting/src/bundle/public.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title></title>
</head>
<body>
<h1>List of Local WebLearn Coordinators</h1>

{{#divisions}}
<h2 id="{{id}}">{{name}}</h2>
{{/divisions}}
</body>
</html>
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package uk.ac.ox.it.vle;

import java.io.InputStream;

/**
* Interface for writing out the report.
* @author Matthew Buckett
*/
interface AdminSiteReportWriter {

enum Access {PRIVATE, PUBLIC};

public void writeReport(String filename, String mimeType, InputStream stream, Access access);

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package uk.ac.ox.it.vle;

import org.sakaiproject.content.api.ContentHostingService;
import org.sakaiproject.content.api.ContentResourceEdit;
import org.sakaiproject.entity.api.Entity;
import org.sakaiproject.exception.*;

import java.io.InputStream;

/**
* Simple implementation that writes the content out into content hosting.
*
* @author Matthew Buckett
*/
public class AdminSiteReportWriterSakai implements AdminSiteReportWriter {

private ContentHostingService contentHostingService;
private String outputFolder;

public void setContentHostingService(ContentHostingService contentHostingService) {
this.contentHostingService = contentHostingService;
}

public void setOutputFolder(String outputFolder) {
// Trim any trailing slash.
if (outputFolder.endsWith(Entity.SEPARATOR)) {
outputFolder = outputFolder.substring(0, outputFolder.length()-1);
}
this.outputFolder = outputFolder;
}

@Override
public void writeReport(String filename, String mimeType, InputStream stream, Access access) {
String resourceId = outputFolder + Entity.SEPARATOR + filename;
try {
try {
contentHostingService.removeResource(resourceId);
} catch (IdUnusedException e) {
// Expected.
}
ContentResourceEdit resource = contentHostingService.addResource(resourceId);
resource.setContent(stream);
resource.setContentType(mimeType);
if (Access.PUBLIC.equals(access)) {
resource.setPublicAccess();
}
contentHostingService.commitResource(resource);
} catch (InUseException e) {
throw new IllegalStateException("Unable to update resource: "+resourceId, e);
} catch (TypeException e) {
throw new IllegalStateException("Unable to update resource: "+resourceId, e);
} catch (PermissionException e) {
throw new IllegalStateException("Unable to update resource: "+resourceId, e);
} catch (IdUsedException e) {
throw new IllegalStateException("Unable to update resource: "+resourceId, e);
} catch (IdInvalidException e) {
throw new IllegalStateException("Unable to update resource: "+resourceId, e);
} catch (InconsistentException e) {
throw new IllegalStateException("Unable to update resource: "+resourceId, e);
} catch (ServerOverloadException e) {
throw new IllegalStateException("Unable to update resource: "+resourceId, e);
} catch (OverQuotaException e) {
throw new IllegalStateException("Unable to update resource: "+resourceId, e);
}
}
}
226 changes: 226 additions & 0 deletions extras/admin-reporting/src/main/uk/ac/ox/it/vle/AdminSitesReport.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,226 @@
package uk.ac.ox.it.vle;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.samskivert.mustache.Mustache;
import com.samskivert.mustache.Template;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.sakaiproject.site.api.Site;
import org.sakaiproject.site.api.SiteService;
import org.sakaiproject.user.api.User;
import org.sakaiproject.user.api.UserDirectoryService;

import java.io.ByteArrayInputStream;
import java.io.InputStreamReader;
import java.io.StringWriter;
import java.util.*;

import static org.sakaiproject.site.api.SiteService.SelectionType;
import static org.sakaiproject.site.api.SiteService.SortType;

/**
* This produces files in content hosting that list the maintainers of an admin site who are happy to have their
* contacts listed.
*
* @author Matthew Buckett
*/
public class AdminSitesReport implements Job {

private static final Log log = LogFactory.getLog(AdminSitesReport.class);
public static final String DEFAULT_SITE_TYPE = "admin";
public static final String DEFAULT_USER_ROLE = "coordinator";
public static final String DEFAULT_DIVISION_PROP = "oxDivision";

private SiteService siteService;
private UserDirectoryService userDirectoryService;
private AdminSiteReportWriter reportWriter;

private String siteType = DEFAULT_SITE_TYPE;
private String userRole = DEFAULT_USER_ROLE;
private Map<String, String> divisionNames = Collections.emptyMap();
private String divisionProp = DEFAULT_DIVISION_PROP;

public void setSiteService(SiteService siteService) {
this.siteService = siteService;
}

public void setUserDirectoryService(UserDirectoryService userDirectoryService) {
this.userDirectoryService = userDirectoryService;
}

public void setReportWriter(AdminSiteReportWriter reportWriter) {
this.reportWriter = reportWriter;
}

public void setSiteType(String siteType) {
this.siteType = siteType;
}

public void setUserRole(String userRole) {
this.userRole = userRole;
}

public void setDivisionNames(Map<String, String> divisionNames) {
this.divisionNames = divisionNames;
}

public void setDivisionProp(String divisionProp) {
this.divisionProp = divisionProp;
}

public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {

List<Site> allSites
= siteService.getSites(SelectionType.ANY, siteType, null, null, SortType.NONE, null);
log.debug(String.format("Found %d sites when search for sites of type %s.", allSites.size(), siteType));

long ignoredSites = 0, ignoredDivisions = 0;

// Split the sites per division
Map<String, List<Site>> sitesByDivision = new HashMap<String, List<Site>>();
for (Site site: allSites) {
String division = site.getProperties().getProperty(divisionProp);
if (divisionNames.containsKey(division)) {
List<Site> divisionSites = sitesByDivision.get(division);
if (divisionSites == null) {
divisionSites = new ArrayList<Site>();
sitesByDivision.put(division, divisionSites);
}
divisionSites.add(site);
} else {
log.debug(String.format("Ignoring sites without an oxDivision: %s(%s)", site.getTitle(), site.getId()));
ignoredSites++;
}
}

// Go through divisions by name
List<Map.Entry<String, String>> divisions = new ArrayList<Map.Entry<String, String>>(divisionNames.entrySet());
Collections.sort(divisions, new MapValueComparator<String>(new NaturalComparator<String>()));
List<String> emails = new ArrayList<String>();

// Used for rendering.
Map<String, Object> context = new HashMap<String, Object>();
ArrayList<Map<String, Object>> divisionsCtx = new ArrayList<Map<String, Object>>();
for (Map.Entry<String, String> divisionEntry: divisions) {
List<Site> sites = sitesByDivision.get(divisionEntry.getKey());
if (sites.size() < 1) {
log.debug("No sites found in division: "+ divisionEntry.getValue());
ignoredDivisions++;
continue;
}
List<Map<String, Object>> sitesCtx = new ArrayList<Map<String, Object>>();
Collections.sort(sites, new SiteTitleComparator());
for (Site site : sites) {
Map<String, Object> siteCtx = new HashMap<String, Object>();
siteCtx.put("title", site.getTitle());
siteCtx.put("id", site.getId());
siteCtx.put("url", site.getUrl());
Set<String> userIds = site.getUsersHasRole(userRole);
List<User> users = userDirectoryService.getUsers(userIds);
List<Map<String,Object>> usersCtx = new ArrayList<Map<String, Object>>(users.size());
for (User user : users) {
Map<String,Object> userCtx = new HashMap<String, Object>();
userCtx.put("name", user.getDisplayName());
userCtx.put("displayId", user.getDisplayId());
usersCtx.add(userCtx);
// Also add to the list of emails.
if (user.getEmail() != null) {
emails.add(user.getEmail());
} else {
log.warn("No email address listed for: "+ user.getDisplayName());
}
}
siteCtx.put("users", usersCtx);
sitesCtx.add(siteCtx);
}
Map<String, Object> divisionCtx = new HashMap<String, Object>();
divisionCtx.put("id", divisionEntry.getKey());
divisionCtx.put("name", divisionEntry.getValue());
divisionCtx.put("sites", sitesCtx);
divisionsCtx.add(divisionCtx);
}
context.put("divisions", divisionsCtx);
context.put("role", userRole);
log.info(String.format("Found %d sites, ignored %d, processed %d divisions, ignored %d",
allSites.size(), ignoredSites, divisions.size(), ignoredDivisions));


Template template = Mustache.compiler().compile(new InputStreamReader(getClass().getResourceAsStream("/public.html")));
StringWriter writer = new StringWriter();
template.execute(context, writer);

reportWriter.writeReport("public.html", "text/html", new ByteArrayInputStream(writer.toString().getBytes()),
AdminSiteReportWriter.Access.PUBLIC);

Gson gson = new GsonBuilder().setPrettyPrinting().create();
writer = new StringWriter();
gson.toJson(context,writer);
reportWriter.writeReport("public.json", "application/json", new ByteArrayInputStream(writer.toString().getBytes()),
AdminSiteReportWriter.Access.PUBLIC);

template = Mustache.compiler().compile(new InputStreamReader(getClass().getResourceAsStream("/emails.txt")));
writer = new StringWriter();
template.execute(Collections.singletonMap("emails", emails), writer);
reportWriter.writeReport("emails.txt", "text/plain", new ByteArrayInputStream(writer.toString().getBytes()),
AdminSiteReportWriter.Access.PRIVATE);

writer = new StringWriter();
gson.toJson(emails, writer);
reportWriter.writeReport("emails.json", "application/json", new ByteArrayInputStream(writer.toString().getBytes()),
AdminSiteReportWriter.Access.PRIVATE);


}

/**
* Just sorts sites by their title.
*/
public static class SiteTitleComparator implements Comparator<Site> {

@Override
public int compare(Site o1, Site o2) {
return o1.getTitle().compareTo(o2.getTitle());
}
}

/**
* A comparator that extracts the map value and passes it to another comparator.
* @param <T> The Comparator that gets the final value.
*/
public static class MapValueComparator<T> implements Comparator<Map.Entry<?,T>> {

private Comparator<T> comparator;

public MapValueComparator(Comparator<T> comparator) {
this.comparator = comparator;
}

@Override
public int compare(Map.Entry<?, T> o1, Map.Entry<?, T> o2) {
return comparator.compare(o1.getValue(), o2.getValue());
}
}

/**
* A comparator that just falls through to the natural ordering of the class.
*
* @param <T> The type that will be compared.
*/
public static class NaturalComparator<T extends Comparable<T>> implements Comparator<T> {

@Override
public int compare(T o1, T o2) {
if( o1 == o2 )
return 0;
if( o1 == null )
return 1;
if( o2 == null )
return -1;
return o1.compareTo( o2 );
}
}
}
Loading

0 comments on commit 607bd92

Please sign in to comment.