/*
* #%L
* Course Signup Implementation
* %%
* Copyright (C) 2010 - 2013 University of Oxford
* %%
* Licensed under the Educational Community License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://opensource.org/licenses/ecl2
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
package uk.ac.ox.oucs.vle;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.FlushMode;
import org.jdom2.Document;
import org.jdom2.JDOMException;
import org.jdom2.input.SAXBuilder;
import org.sakaiproject.util.FormattedText;
import org.springframework.orm.hibernate3.HibernateAccessor;
import org.xcri.Extension;
import org.xcri.common.ExtensionManager;
import org.xcri.common.OverrideManager;
import org.xcri.common.Title;
import org.xcri.core.Catalog;
import org.xcri.core.Course;
import org.xcri.core.Presentation;
import org.xcri.core.Provider;
import org.xcri.exceptions.InvalidElementException;
import org.xcri.presentation.Venue;
import org.xcri.types.DescriptiveTextType;
import uk.ac.ox.oucs.vle.xcri.daisy.Bookable;
import uk.ac.ox.oucs.vle.xcri.daisy.CourseSubUnit;
import uk.ac.ox.oucs.vle.xcri.daisy.DepartmentThirdLevelApproval;
import uk.ac.ox.oucs.vle.xcri.daisy.DepartmentalSubUnit;
import uk.ac.ox.oucs.vle.xcri.daisy.DivisionWideEmail;
import uk.ac.ox.oucs.vle.xcri.daisy.EmployeeEmail;
import uk.ac.ox.oucs.vle.xcri.daisy.EmployeeName;
import uk.ac.ox.oucs.vle.xcri.daisy.Identifier;
import uk.ac.ox.oucs.vle.xcri.daisy.ModuleApproval;
import uk.ac.ox.oucs.vle.xcri.daisy.OtherDepartment;
import uk.ac.ox.oucs.vle.xcri.daisy.Sessions;
import uk.ac.ox.oucs.vle.xcri.daisy.SupervisorApproval;
import uk.ac.ox.oucs.vle.xcri.daisy.TeachingDetails;
import uk.ac.ox.oucs.vle.xcri.daisy.TermCode;
import uk.ac.ox.oucs.vle.xcri.daisy.TermLabel;
import uk.ac.ox.oucs.vle.xcri.daisy.WebAuthCode;
import uk.ac.ox.oucs.vle.xcri.oxcap.MemberApplyTo;
import uk.ac.ox.oucs.vle.xcri.oxcap.OxcapCourse;
import uk.ac.ox.oucs.vle.xcri.oxcap.OxcapPresentation;
import uk.ac.ox.oucs.vle.xcri.oxcap.Session;
import uk.ac.ox.oucs.vle.xcri.oxcap.Subject;
public class XcriOxCapPopulatorImpl implements Populator {
/**
* The DAO to update our entries through.
*/
private CourseDAO dao;
public void setCourseDao(CourseDAO dao) {
this.dao = dao;
}
/**
* The proxy for getting users.
*/
private SakaiProxy proxy;
public void setProxy(SakaiProxy proxy) {
this.proxy = proxy;
}
/**
* The class to get the inputStream.
*/
private PopulatorInput populatorInput;
public void setPopulatorInput(PopulatorInput populatorInput) {
this.populatorInput = populatorInput;
}
/**
* The search service
*/
private SearchService search;
public void setSearchService(SearchService search) {
this.search = search;
}
private static final Log log = LogFactory.getLog(XcriOxCapPopulatorImpl.class);
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("dd MMMM yyyy hh:mm");
private static String TARGET_AUDIENCE = "cdp:targetAudience";
static {
ExtensionManager.registerExtension(new WebAuthCode());
ExtensionManager.registerExtension(new DepartmentalSubUnit());
ExtensionManager.registerExtension(new DepartmentThirdLevelApproval());
ExtensionManager.registerExtension(new DivisionWideEmail());
ExtensionManager.registerExtension(new CourseSubUnit());
ExtensionManager.registerExtension(new ModuleApproval());
ExtensionManager.registerExtension(new SupervisorApproval());
ExtensionManager.registerExtension(new OtherDepartment());
ExtensionManager.registerExtension(new Sessions());
ExtensionManager.registerExtension(new Bookable());
ExtensionManager.registerExtension(new TermCode());
ExtensionManager.registerExtension(new TermLabel());
ExtensionManager.registerExtension(new EmployeeName());
ExtensionManager.registerExtension(new EmployeeEmail());
ExtensionManager.registerExtension(new Identifier());
ExtensionManager.registerExtension(new MemberApplyTo());
ExtensionManager.registerExtension(new TeachingDetails());
ExtensionManager.registerExtension(new Subject());
ExtensionManager.registerExtension(new Session());
OverrideManager.registerOverride(Course.class, new OxcapCourse());
OverrideManager.registerOverride(Presentation.class, new OxcapPresentation());
}
/**
* @throws
* @throws MalformedURLException
*
*/
public void update(PopulatorContext context) throws PopulatorException {
InputStream input = null;
try {
// This is so that collections don't get lost, this is a hack but I couldn't find the
// simple fix to get it working.
dao.setFlushMode(HibernateAccessor.FLUSH_EAGER);
input = populatorInput.getInput(context);
if (null == input) {
throw new PopulatorException("No Input for Importer");
}
process(context, input);
} catch (MalformedURLException e) {
log.warn("MalformedURLException ["+context.getURI()+"]", e);
throw new PopulatorException(e.getLocalizedMessage());
} catch (IllegalStateException e) {
log.warn("IllegalStateException ["+context.getURI()+"]", e);
throw new PopulatorException(e.getLocalizedMessage());
} catch (IOException e) {
log.warn("IOException ["+context.getURI()+"]", e);
throw new PopulatorException(e.getLocalizedMessage());
} catch (JDOMException e) {
log.warn("JDOMException ["+context.getURI()+"]", e);
throw new PopulatorException(e.getLocalizedMessage());
} catch (InvalidElementException e) {
log.warn("InvalidElementException ["+context.getURI()+"]", e);
throw new PopulatorException(e.getLocalizedMessage());
} finally {
if (null != input) {
try {
input.close();
} catch (IOException e) {
log.error("IOException ["+e+"]");
}
}
}
}
/**
* Attempts to create a category mapper using config from the SakaiProxy, or
* if that one isn't found config from the classpath. If no mappings can be found anywhere
* it still returns a mapper, but that mapper doesn't alter anything.
*
* @return A CategoryMapper and never <code>null</code>.
*/
private CategoryMapper getCategoryMapper() {
CategoryMapper mapper = new CategoryMapper();
Properties props = proxy.getCategoryMapping();
if (props == null) {
// fallback to classpath.
String classpathResource =
proxy.getConfigParam("course-signup.default-category-mapping", "/default-category-mapping.properties");
props = new Properties();
try {
props.load(getClass().getResourceAsStream(classpathResource));
} catch (IOException e) {
log.error("Failed to load default category mapping from classpath using: "+ classpathResource+
" No mapping of categories will be done", e);
}
}
if (props != null) {
mapper.setMappings(props);
}
return mapper;
}
/**
*
* @param inputStream
* @throws IOException
* @throws JDOMException
* @throws InvalidElementException
*/
public void process(PopulatorContext context, InputStream inputStream)
throws JDOMException, IOException, InvalidElementException {
Catalog catalog = new Catalog();
SAXBuilder builder = new SAXBuilder();
Document document = builder.build(inputStream);
catalog.fromXml(document);
// Here we setup our category mapper, we don't have this as a singleton managed by spring because
// the content hosting service isn't up when spring does init() on a singleton.
// We could have followed that route and done lazy loading, but then you still need to manage
// reload when someone changes the file. This way it's loaded for every import.
CategoryMapper mapper = getCategoryMapper();
PopulatorInstanceData data = new PopulatorInstanceData(mapper);
if (null != context.getDeletedLogWriter()) {
context.getDeletedLogWriter().heading(catalog.getGenerated());
}
if (null != context.getErrorLogWriter()) {
context.getErrorLogWriter().heading(catalog.getGenerated());
}
if (null != context.getInfoLogWriter()) {
context.getInfoLogWriter().heading(catalog.getGenerated());
}
Provider[] providers = catalog.getProviders();
for (Provider provider : providers) {
provider(provider, context, data);
}
logMs(context,
"CourseDepartments (seen: "+ data.getDepartmentSeen() + " created: "+ data.getDepartmentCreated() + ", updated: "+ data.getDepartmentUpdated() +")");
logMs(context,
"CourseSubUnits (seen: "+ data.getSubunitSeen() + " created: "+ data.getSubunitCreated() + ", updated: "+ data.getSubunitUpdated() +")");
logMs(context,
"CourseGroups (seen: "+ data.getGroupSeen() + " created: "+ data.getGroupCreated() + ", updated: "+ data.getGroupUpdated() +")");
logMs(context,
"CourseComponents (seen: "+ data.getComponentSeen() + " created: "+ data.getComponentCreated() + ", updated: "+ data.getComponentUpdated() +")");
}
/**
*
* @param provider
* @throws IOException
*/
private void provider(Provider provider, PopulatorContext context, PopulatorInstanceData data)
throws IOException {
String departmentName = null;
if (provider.getTitles().length > 0) {
departmentName = provider.getTitles()[0].getValue();
}
String departmentCode = null;
String divisionEmail = null;
boolean departmentApproval = false;
String divisionCode = null;
Set<String> departmentApprovers = new HashSet<String>();
Set<String> divisionSuperUsers = new HashSet<String>();
Map<String, String> subunits = new HashMap<String, String>();
for (Extension extension : provider.getExtensions()) {
if (extension instanceof Identifier) {
Identifier identifier = (Identifier) extension;
if (typeProviderId(identifier.getType())) {
if (typeProviderFallbackId(identifier.getType()) &&
null != departmentCode) {
continue;
}
departmentCode = identifier.getValue();
continue;
}
if (typeProviderDivision(identifier.getType())) {
divisionCode = identifier.getValue();
continue;
}
}
if (extension instanceof DivisionWideEmail) {
divisionEmail = extension.getValue();
continue;
}
if (extension instanceof DepartmentThirdLevelApproval) {
departmentApproval = parseBoolean(extension.getValue());
continue;
}
if (extension instanceof ModuleApproval) {
departmentApprovers.add(getUser(extension.getValue()));
continue;
}
if (extension instanceof WebAuthCode) {
WebAuthCode webAuthCode = (WebAuthCode) extension;
if (webAuthCode.getWebAuthCodeType() == WebAuthCode.WebAuthCodeType.superUser) {
divisionSuperUsers.add(getUser(webAuthCode.getValue()));
}
continue;
}
if (extension instanceof DepartmentalSubUnit) {
DepartmentalSubUnit subUnit = (DepartmentalSubUnit) extension;
subunits.put(subUnit.getCode(), subUnit.getValue());
continue;
}
}
if (null == departmentCode) {
logMe(context,
"Log Failure Provider ["+departmentCode+":"+departmentName+"] No Provider Identifier");
return;
}
data.incrDepartmentSeen();
if (updateDepartment(departmentCode, departmentName, departmentApproval,
departmentApprovers)) {
data.incrDepartmentCreated();;
} else {
data.incrDepartmentUpdated();
}
for (Map.Entry<String, String> entry : subunits.entrySet()) {
data.incrSubunitSeen();
if (updateSubUnit(entry.getKey(), entry.getValue(), departmentCode)) {
data.incrSubunitCreated();;
} else {
data.incrSubunitUpdated();
}
}
for (Course course : provider.getCourses()) {
course(course, departmentCode, departmentName, divisionEmail, divisionSuperUsers, context, data);
}
}
/**
*
* @param type
* @return
*/
protected static boolean typeProviderId(String type) {
if ("ns:department".equals(type) ||
"ns:twoThree".equals(type)) {
return true;
}
return false;
}
protected static boolean typeProviderFallbackId(String type) {
if ("ns:department".equals(type)) {
return true;
}
return false;
}
/**
*
* @param type
* @return
*/
protected static boolean typeProviderDivision(String type) {
if ("ns:division".equals(type)) {
return true;
}
return false;
}
/**
* Process <course> tag
*
* @param course
* @param departmentCode
* @param departmentName
* @param divisionEmail
* @param divisionSuperUsers
* @throws IOException
*/
private void course(Course course,
String departmentCode, String departmentName,
String divisionEmail, Set<String> divisionSuperUsers,
PopulatorContext context,
PopulatorInstanceData data)
throws IOException {
CourseGroupDAO myCourse = new CourseGroupDAO();
myCourse.setSource(context.getName());
myCourse.setDept(departmentCode);
myCourse.setDepartmentName(departmentName);
myCourse.setContactEmail(divisionEmail);
myCourse.setTitle(course.getTitles()[0].getValue());
OxcapCourse oxCourse = (OxcapCourse)course;
myCourse.setVisibility(oxCourse.getVisibility().toString());
myCourse.setDescription(filterDescriptiveTextTypeArray(course.getDescriptions(), null));
myCourse.setPrerequisite(filterDescriptiveTextTypeArray(course.getDescriptions(), TARGET_AUDIENCE));
myCourse.setRegulations(filterDescriptiveTextTypeArray(course.getRegulations(), null));
Set<Subject.SubjectIdentifier> categories = new HashSet<Subject.SubjectIdentifier>();
String teachingComponentId = null;
Set<String> administrators = new HashSet<String>();
Set<String> otherDepartments = new HashSet<String>();
for (Extension extension : course.getExtensions()) {
if (extension instanceof Identifier) {
Identifier identifier = (Identifier) extension;
if (typeCourseId(identifier.getType())) {
myCourse.setCourseId(identifier.getValue());
}
if ("teachingComponentId".equals(identifier.getType())) {
teachingComponentId = identifier.getValue();
}
continue;
}
if (extension instanceof SupervisorApproval) {
myCourse.setSupervisorApproval(parseBoolean(extension.getValue()));
continue;
}
if (extension instanceof ModuleApproval) {
myCourse.setAdministratorApproval(parseBoolean(extension.getValue()));
continue;
}
if (extension instanceof CourseSubUnit) {
CourseSubUnit subUnit = (CourseSubUnit)extension;
myCourse.setSubunit(subUnit.getCode());
myCourse.setSubunitName(subUnit.getValue());
continue;
}
if (extension instanceof WebAuthCode) {
WebAuthCode webAuthCode = (WebAuthCode) extension;
if (webAuthCode.getWebAuthCodeType() == WebAuthCode.WebAuthCodeType.administrator) {
administrators.add(getUser(webAuthCode.getValue()));
}
continue;
}
if (extension instanceof OtherDepartment) {
if (!extension.getValue().isEmpty()) {
otherDepartments.add(extension.getValue());
}
continue;
}
if (extension instanceof Subject) {
Subject subject = (Subject) extension;
// Check it's a in our constrained vocab
Subject.SubjectIdentifier subjectIdentifier = subject.getSubjectIdentifier();
if (subjectIdentifier != null && subject.isValid()) {
categories.add(subjectIdentifier);
} else {
log.debug(String.format("Ignoring subject of %s", subject));
}
}
}
// Map existing course categories onto vitae course categories.
data.getMapper().mapCategories(categories);
myCourse.setAdministrators(administrators);
myCourse.setOtherDepartments(otherDepartments);
myCourse.setSuperusers(divisionSuperUsers);
if (null == myCourse.getCourseId()) {
logMe(context,
"Log Failure Course ["+myCourse.getCourseId()+":"+myCourse.getTitle()+"] No Course Identifier");
return;
}
if (myCourse.getDescription().isEmpty()) {
logMe(context,
"Log Warning Course ["+myCourse.getCourseId()+":"+myCourse.getTitle()+"] has no description");
}
if (!myCourse.getCourseId().equals(data.getLastGroup())) {
data.incrGroupSeen();
data.setLastGroup(myCourse.getCourseId());
if (validCourse(context, data, myCourse)) {
updateCourse(context, data, myCourse);
}
}
Presentation[] presentations = course.getPresentations();
for (int i=0; i<presentations.length; i++) {
presentation(presentations[i], myCourse.getCourseId(), teachingComponentId, context, data);
}
for (Subject.SubjectIdentifier subjectIdentifier : categories) {
CourseGroup.CategoryType type = null;
if (subjectIdentifier instanceof Subject.RMSubjectIdentifier) {
type = CourseGroup.CategoryType.RM;
} else if (subjectIdentifier instanceof Subject.VITAESubjectIdentifier) {
type = CourseGroup.CategoryType.VITAE;
} else if (subjectIdentifier instanceof Subject.RDFSubjectIdentifier) {
type = CourseGroup.CategoryType.RDF;
} else {
log.warn("Unknown subject identifier "+ subjectIdentifier);
continue;
}
updateCategory(context, new CourseCategoryDAO(
type, subjectIdentifier.name(), subjectIdentifier.getValue()),
myCourse.getCourseId());
}
/**
* Update the search engine
*/
if (null != search) {
CourseGroupDAO courseDao = dao.findCourseGroupById(myCourse.getCourseId());
search.addCourseGroup(new CourseGroupImpl(courseDao, null));
}
}
/**
*
* @param type
* @return
*/
protected static boolean typeCourseId(String type) {
return ("ns:daisy-course".equals(type) ||
"ns:itlp-course".equals(type) ||
"ns:careers-course".equals(type) ||
"ns:language-centre-course".equals(type) ||
"ns:medsci-course".equals(type) ||
"ns:humanities-course".equals(type) ||
"ns:oli-course".equals(type) ||
"ns:sharepoint-course".equals(type)
);
}
/**
*
* @param presentation
* @param teachingcomponentId
* @throws IOException
*/
private void presentation(Presentation presentation,
String assessmentunitCode, String teachingcomponentId,
PopulatorContext context,
PopulatorInstanceData data)
throws IOException {
CourseComponentDAO myPresentation = new CourseComponentDAO();
myPresentation.setComponentId(teachingcomponentId);
myPresentation.setSource(context.getName());
myPresentation.setTitle(presentation.getTitles()[0].getValue());
if (null != presentation.getAttendanceMode()) {
myPresentation.setAttendanceMode(presentation.getAttendanceMode().getIdentifier());
myPresentation.setAttendanceModeText(presentation.getAttendanceMode().getValue());
}
if (null != presentation.getAttendancePattern()) {
myPresentation.setAttendancePattern(presentation.getAttendancePattern().getIdentifier());
myPresentation.setAttendancePatternText(presentation.getAttendancePattern().getValue());
}
if (null != presentation.getApplyTo()) {
myPresentation.setApplyTo(presentation.getApplyTo().getValue());
}
if (null != presentation.getStart()) {
myPresentation.setStarts(presentation.getStart().getDtf());
myPresentation.setStartsText(presentation.getStart().getValue());
}
if (null != presentation.getEnd()) {
myPresentation.setEnds(presentation.getEnd().getDtf());
myPresentation.setEndsText(presentation.getEnd().getValue());
}
if (null != presentation.getApplyFrom()) {
myPresentation.setOpens(presentation.getApplyFrom().getDtf());
myPresentation.setOpensText(presentation.getApplyFrom().getValue());
}
if (null != presentation.getApplyUntil()) {
myPresentation.setCloses(presentation.getApplyUntil().getDtf());
myPresentation.setClosesText(presentation.getApplyUntil().getValue());
}
if (0 != presentation.getVenues().length) {
Venue venue = presentation.getVenues()[0];
if (null != venue.getProvider() && venue.getProvider().getTitles().length > 0) {
myPresentation.setLocation(venue.getProvider().getTitles()[0].getValue());
}
}
Set<Session> sessions = new HashSet<Session>();
for (Extension extension : presentation.getExtensions()) {
if (extension instanceof Identifier) {
Identifier identifier = (Identifier) extension;
if ("presentationURI".equals(identifier.getType())) {
//uri = identifier.getValue();
continue;
}
if (typePresentationId(identifier.getType())) {
myPresentation.setPresentationId(identifier.getValue());
continue;
}
}
if (extension instanceof Bookable) {
myPresentation.setBookable(parseBoolean(extension.getValue()));
continue;
}
if (extension instanceof EmployeeName) {
myPresentation.setTeacherName(extension.getValue());
continue;
}
if (extension instanceof EmployeeEmail) {
myPresentation.setTeacherEmail(extension.getValue());
continue;
}
if (extension instanceof MemberApplyTo) {
myPresentation.setMemberApplyTo(extension.getValue());
continue;
}
if (extension instanceof Sessions) {
myPresentation.setSessions(extension.getValue());
continue;
}
if (extension instanceof TermCode) {
// TODO We should validate it here.
// TT09, HT09, MT09, TT10
myPresentation.setTermcode(extension.getValue());
continue;
}
if (extension instanceof TeachingDetails) {
myPresentation.setTeachingDetails(extension.getValue());
continue;
}
if (extension instanceof WebAuthCode) {
WebAuthCode webAuthCode = (WebAuthCode) extension;
if (webAuthCode.getWebAuthCodeType() == WebAuthCode.WebAuthCodeType.presenter) {
myPresentation.setTeacher(getUser(webAuthCode.getValue()));
}
continue;
}
if (extension instanceof Session) {
Session session = (Session)extension;
sessions.add(session);
continue;
}
}
if (null != presentation.getPlaces() &&
!presentation.getPlaces().getValue().isEmpty()) {
try {
myPresentation.setSize(Integer.parseInt(presentation.getPlaces().getValue()));
} catch (Exception e) {
logMs(context,
"Log Warning Presentation ["+
myPresentation.getPresentationId()+":"+myPresentation.getTitle()+
"] value in places tag is not a number ["+presentation.getPlaces().getValue()+"]");
}
}
CourseGroupDAO courseDao = dao.findCourseGroupById(assessmentunitCode);
data.incrComponentSeen();
if (validComponent(context, data, myPresentation, courseDao)) {
Iterator<Session> sessionIterator = sessions.iterator();
while (sessionIterator.hasNext()) {
Session session = sessionIterator.next();
if (! (validSession(context, session, myPresentation))) {
// Logging is done in the validate.
sessionIterator.remove();
}
}
updateComponent(context, data, myPresentation, sessions, courseDao);
}
}
/**
*
* @param type
* @return
*/
protected static boolean typePresentationId(String type) {
return ("ns:daisy-presentation".equals(type) ||
"ns:careers-presentation".equals(type) ||
"ns:itlp-presentation".equals(type) ||
"ns:language-centre-presentation".equals(type) ||
"ns:medsci-presentation".equals(type) ||
"ns:sharepoint-presentation".equals(type) ||
"ns:oli-presentation".equals(type) ||
"ns:humanities-presentation".equals(type)
);
}
/**
*
* @param code
* @param name
* @param approve
* @param approvers
* @return
*/
private boolean updateDepartment(String code, String name, boolean approve, Set<String> approvers) {
log.debug("XcriPopulatorImpl.updateDepartment ["+code+":"+name+":"+
approve+":"+approvers.size()+"]");
boolean created = false;
if (null != dao) {
CourseDepartmentDAO departmentDao = dao.findDepartmentByCode(code);
if (null == departmentDao) {
departmentDao = new CourseDepartmentDAO(code);
created = true;
}
departmentDao.setName(name);
departmentDao.setApprove(approve);
departmentDao.setApprovers(approvers);
dao.save(departmentDao);
}
return created;
}
/**
*
* @param code
* @param name
* @param departmentCode
* @return
*/
private boolean updateSubUnit(String code, String name, String departmentCode) {
log.debug("XcriPopulatorImpl.updateSubUnit ["+
code+":"+name+":"+departmentCode+"]");
boolean created = false;
if (null != dao) {
CourseSubunitDAO subunitDao = dao.findSubunitByCode(code);
if (null == subunitDao) {
subunitDao = new CourseSubunitDAO(code);
created = true;
}
subunitDao.setSubunitName(name);
subunitDao.setDepartmentCode(departmentCode);
dao.save(subunitDao);
}
return created;
}
/**
*
* @param data
* @param myCourse
* @return
*/
protected boolean validCourse(PopulatorContext context,
PopulatorInstanceData data,
CourseGroupDAO myCourse) {
int i=0;
try {
if (null == myCourse.getCourseId()) {
logMe(context, "Log Failure Assessment Unit ["+myCourse.getCourseId()+":"+myCourse.getTitle()+"] No AssessmentUnit code");
i++;
}
if (i == 0) {
return true;
}
} catch (IOException e) {
}
return false;
}
/**
*
* @param data
* @param myCourse
* @return
* @throws IOException
*/
private boolean updateCourse(PopulatorContext context,
PopulatorInstanceData data,
CourseGroupDAO myCourse) throws IOException {
boolean created = false;
if (null != dao) {
CourseGroupDAO groupDao = dao.findCourseGroupById(myCourse.getCourseId());
if (groupDao == null) {
groupDao = dao.newCourseGroup(myCourse.getCourseId(), myCourse.getTitle(), myCourse.getDept(), myCourse.getSubunit());
created = true;
} else {
groupDao.setDept(myCourse.getDept());
groupDao.setSubunit(myCourse.getSubunit());
groupDao.setTitle(myCourse.getTitle());
}
groupDao.setDescription(myCourse.getDescription());
groupDao.setDepartmentName(myCourse.getDepartmentName());
groupDao.setSubunitName(myCourse.getSubunitName());
groupDao.setVisibility(myCourse.getVisibility());
groupDao.setSource(myCourse.getSource());
groupDao.setSupervisorApproval(myCourse.getSupervisorApproval());
groupDao.setAdministratorApproval(myCourse.getAdministratorApproval());
groupDao.setContactEmail(myCourse.getContactEmail());
groupDao.getAdministrators().clear();
groupDao.getAdministrators().addAll(myCourse.getAdministrators());
groupDao.setPrerequisite(myCourse.getPrerequisite());
groupDao.setRegulations(myCourse.getRegulations());
groupDao.setDeleted(false);
groupDao.getSuperusers().clear();
groupDao.getSuperusers().addAll(myCourse.getSuperusers());
groupDao.getOtherDepartments().clear();
groupDao.getOtherDepartments().addAll(myCourse.getOtherDepartments());
dao.save(groupDao);
}
if (created) {
logMs(context, "Log Success Course Group created ["+myCourse.getCourseId()+":"+myCourse.getTitle()+"]");
data.incrGroupCreated();
} else {
logMs(context, "Log Success Course Group updated ["+myCourse.getCourseId()+":"+myCourse.getTitle()+"]");
data.incrGroupUpdated();
}
return created;
}
/**
* This validates that a session is correctly populated.
*
* @param context The populator context (used for logging).
* @param session The session to validate.
* @param myPresentation The presentation this is linked to so we can log against it (presentation shoudl be valid).
* @return <code>true</code> if the session is good.
*/
private boolean validSession(PopulatorContext context, Session session, CourseComponentDAO myPresentation) {
int i = 0;
try {
if (session.getIdentifiers().length == 0) {
logMe(context, "Log Failure Session for Teaching Instance [" + myPresentation.getPresentationId() + "] No session identifier");
i++;
} else {
String id = session.getIdentifiers()[0].getValue();
if (id == null || id.length() == 0) {
logMe(context, "Log Failure Session for Teaching Instance [" + myPresentation.getPresentationId() + "] Empty session identifier");
i++;
}
}
if (session.getStart() == null) {
logMe(context, "Log Failure Session for Teaching Instance [" + myPresentation.getPresentationId() + "] No start date");
i++;
}
if (session.getEnd() == null) {
logMe(context, "Log Failure Session for Teaching Instance [" + myPresentation.getPresentationId() + "] No end date");
i++;
}
return i == 0;
} catch (IOException ioe) {
log.warn("Failed to write log: "+ ioe.getMessage());
}
return false;
}
/**
*
* @param data
* @param myPresentation
* @return
*/
protected boolean validComponent(PopulatorContext context,
PopulatorInstanceData data,
CourseComponentDAO myPresentation,
CourseGroupDAO group) {
int i=0;
try {
if (myPresentation.getPresentationId() == null || myPresentation.getPresentationId().isEmpty()) {
logMe(context, "Log Failure Teaching Instance ["+myPresentation.getPresentationId()+":"+myPresentation.getTitle()+"] No presentation ID");
i++;
}
if (null != myPresentation.getOpens() && null != myPresentation.getCloses()) {
if (myPresentation.getOpens().after(myPresentation.getCloses())){
logMe(context, "Log Failure Teaching Instance ["+myPresentation.getPresentationId()+":"+myPresentation.getTitle()+"] Open date is after close date");
i++;
}
}
if (myPresentation.getTitle() == null || myPresentation.getTitle().trim().length() == 0) {
logMe(context, "Log Failure Teaching Instance ["+myPresentation.getPresentationId()+":"+myPresentation.getTitle()+"] Title isn't set");
i++;
}
if (null == group) {
logMe(context, "Log Failure Teaching Instance ["+myPresentation.getPresentationId()+":"+myPresentation.getTitle()+"] No Assessment Unit codes");
i++;
}
if (i == 0) {
return true;
}
} catch (IOException e) {
log.warn("Failed to write log: "+ e.getMessage());
}
return false;
}
/**
*
* @param data
* @param myPresentation
* @param sessions
* @return
* @throws IOException
*/
private boolean updateComponent(PopulatorContext context,
PopulatorInstanceData data,
CourseComponentDAO myPresentation,
Set<Session> sessions, CourseGroupDAO group) throws IOException {
boolean created = false;
if (null != dao) {
CourseComponentDAO componentDao = dao.findCourseComponent(myPresentation.getPresentationId());
if (componentDao == null) {
componentDao = dao.newCourseComponent(myPresentation.getPresentationId());
created = true;
}
componentDao.setTitle(myPresentation.getTitle());
componentDao.setOpens(myPresentation.getOpens());
componentDao.setOpensText(myPresentation.getOpensText());
componentDao.setCloses(myPresentation.getCloses());
componentDao.setClosesText(myPresentation.getClosesText());
componentDao.setStarts(myPresentation.getStarts());
componentDao.setStartsText(myPresentation.getStartsText());
componentDao.setEnds(myPresentation.getEnds());
componentDao.setEndsText(myPresentation.getEndsText());
componentDao.setBookable(myPresentation.isBookable());
componentDao.setSize(myPresentation.getSize());
componentDao.setTermcode(myPresentation.getTermcode());
componentDao.setAttendanceMode(myPresentation.getAttendanceMode());
componentDao.setAttendanceModeText(myPresentation.getAttendanceModeText());
componentDao.setAttendancePattern(myPresentation.getAttendancePattern());
componentDao.setAttendancePatternText(myPresentation.getAttendancePatternText());
componentDao.setComponentId(myPresentation.getComponentId()+":"+myPresentation.getTermcode());
componentDao.setTeacher(myPresentation.getTeacher());
componentDao.setTeacherName(myPresentation.getTeacherName());
componentDao.setTeacherEmail(myPresentation.getTeacherEmail());
componentDao.setSessions(myPresentation.getSessions());
componentDao.setLocation(myPresentation.getLocation());
componentDao.setApplyTo(myPresentation.getApplyTo());
componentDao.setMemberApplyTo(myPresentation.getMemberApplyTo());
componentDao.setTeachingDetails(myPresentation.getTeachingDetails());
componentDao.setBaseDate(baseDate(componentDao));
componentDao.setSource(myPresentation.getSource());
// Populate teacher details.
// Look for details in WebLearn first then fallback to details in DAISY.
if (myPresentation.getTeacher() != null && myPresentation.getTeacher().length() > 0) {
UserProxy teacher = proxy.findUserByEid(myPresentation.getTeacher());
if (teacher != null) {
componentDao.setTeacherName(teacher.getDisplayName());
componentDao.setTeacherEmail(teacher.getEmail());
}
}
// Use of Set filters duplicates
componentDao.getGroups().add(group);
componentDao.setDeleted(false);
componentDao.getComponentSessions().clear();
for (Session session : sessions) {
if (session.getIdentifiers().length < 1) {
logMe(context, "Skipped session because no identifier found ["
+myPresentation.getPresentationId()+":"+myPresentation.getTitle()+"]");
continue;
}
String identifier = session.getIdentifiers()[0].getValue();
List<String> locations = new ArrayList<String>(2);
for (Venue venue: session.getVenues()) {
if (venue.getProvider() != null) {
for (Title title : venue.getProvider().getTitles()) {
locations.add(title.getValue());
}
}
}
if (locations.size() > 1) {
logMs(context, "More than one location found for session:"+ identifier + " ["
+myPresentation.getPresentationId()+":"+myPresentation.getTitle()+"]");
}
String location = StringUtils.trimToNull(StringUtils.join(locations, " "));
componentDao.getComponentSessions().add(
new CourseComponentSessionDAO(identifier,
session.getStart().getDtf(), session.getStart().getValue(),
session.getEnd().getDtf(), session.getEnd().getValue(), location));
}
if (!componentDao.getComponentSessions().isEmpty()) {
componentDao.setSessions(Integer.toString(componentDao.getComponentSessions().size()));
}
dao.save(componentDao);
}
if (created) {
logMs(context, "Log Success Course Component created ["+myPresentation.getPresentationId()+":"+myPresentation.getTitle()+"]");
data.incrComponentCreated();
} else {
logMs(context, "Log Success Course Component updated ["+myPresentation.getPresentationId()+":"+myPresentation.getTitle()+"]");
data.incrComponentUpdated();
}
return created;
}
/*
*
*/
private boolean updateCategory(PopulatorContext context,CourseCategoryDAO category, String assessmentunitCode) throws IOException {
boolean created = false;
if (null == category.getCategoryId() || category.getCategoryId().isEmpty()) {
logMe(context, "Category ["+category.getCategoryType()+":"+category.getCategoryName()+
"] ignored on course ["+assessmentunitCode+"] - empty identifier");
return created;
}
if (null != dao) {
CourseCategoryDAO categoryDao = dao.findCourseCategory(category.getCategoryId());
if (categoryDao == null) {
// We have a race condition here when multiple threads attempt to create the same category
categoryDao = category;
created = true;
dao.save(categoryDao);
}
CourseGroupDAO courseDao = dao.findCourseGroupById(assessmentunitCode);
courseDao.getCategories().add(categoryDao);
dao.save(courseDao);
}
return created;
}
/**
* @throws IOException
*
*/
private void logMe(PopulatorContext context, String message) throws IOException {
log.info(message);
if (null != context.getErrorLogWriter()) {
context.getErrorLogWriter().write(message+"\n");
}
}
/**
* @throws IOException
*
*/
private void logMs(PopulatorContext context, String message) throws IOException {
log.info(message);
if (null != context.getInfoLogWriter()) {
context.getInfoLogWriter().write(message+"\n");
}
}
/**
*
* @param userCode
* @return
*/
private String getUser (String userCode) {
if (null == proxy) {
return userCode;
}
UserProxy user = proxy.findUserByEid(userCode);
if (null == user) {
log.warn("Failed to find User [" + userCode +"]");
return null;
}
return user.getId();
}
protected static String viewDate(Date date, String text) {
if (null == date) {
return text+"[null]";
}
SimpleDateFormat sdf = new SimpleDateFormat("dd-MM-yyyy");
return text+"["+sdf.format(date)+"]";
}
private static boolean parseBoolean(String data) {
if ("1".equals(data)) {
return true;
}
if ("0".equals(data)) {
return false;
}
return Boolean.parseBoolean(data);
}
/**
* Find the base date for a component.
* The base date is the point at which a course transitions from being a current course to a previous course.
*
* @param component
* the component to assess.
* @return
* the base date for the component or <code>null</code> if it's not possible to find one.
*/
public static Date baseDate(CourseComponentDAO component) {
if (null != component.getEnds()) {
return component.getEnds();
}
if (null != component.getCloses()) {
return component.getCloses();
}
return null;
}
/**
* Search the array for elements matching type and add concatenate their descriptions.
*
* @param array
* The array of DescriptiveTextTypes
* @param type
* The type to filter on, if <code>null</code> look for text without a type (<code>null</code>).
* @return
* The concatenated matching descriptions.
*/
protected String filterDescriptiveTextTypeArray(DescriptiveTextType[] array, String type) {
StringBuilder sb = new StringBuilder();
for (DescriptiveTextType descriptiveTextType: array) {
String text;
if (!descriptiveTextType.isXhtml()) {
text = parse(descriptiveTextType.getValue());
} else {
text = parseXHTML(descriptiveTextType.getValue());
}
if (null != type) {
if (type.equals(descriptiveTextType.getType())) {
sb.append(text).append(" ");
}
} else {
if (null == descriptiveTextType.getType()) {
sb.append(text).append(" ");
}
}
}
return sb.toString().trim();
}
/**
* * Processing of descriptivetext fields where descriptiveTextType.isXhtml=false
*
* @param data
* @return
*/
static String parse(String data) {
data = data.replaceAll("<", "<");
data = data.replaceAll(">", ">");
data = FormattedText.convertPlaintextToFormattedText(data);
Pattern pattern = Pattern.compile("[A-Z0-9._%+-]+@[A-Z0-9.-]+\\.[A-Z]{2,4}", Pattern.CASE_INSENSITIVE);
Matcher matcher = pattern.matcher(data);
StringBuffer sb = new StringBuffer(data.length());
while (matcher.find()) {
String text = matcher.group(0);
matcher.appendReplacement(sb, "<a class=\"email\" href=\"mailto:"+text+"\">"+text+"</a>" );
}
matcher.appendTail(sb);
pattern = Pattern.compile("(https?|ftps?):\\/\\/[a-z_0-9\\\\\\-]+(\\.([\\w#!:?+=&%@!\\-\\/])+)+", Pattern.CASE_INSENSITIVE);
matcher = pattern.matcher(sb.toString());
sb = new StringBuffer(data.length());
while (matcher.find()) {
String text = matcher.group(0);
matcher.appendReplacement(sb, "<a class=\"url\" href=\""+text+"\" target=\"_blank\">"+text+"</a>" );
}
matcher.appendTail(sb);
return sb.toString();
}
/**
* Processing of descriptivetext fields where descriptiveTextType.isXhtml=true
*
* @param data
* @return
*/
static String parseXHTML(String data) {
if (null != data) {
data = data.replaceAll("xhtml:", "");
}
return data;
}
}