org.cloudcoder.dataanalysis.ProgsnapExport.java Source code

Java tutorial

Introduction

Here is the source code for org.cloudcoder.dataanalysis.ProgsnapExport.java

Source

// CloudCoder - a web-based pedagogical programming environment
// Copyright (C) 2011-2015, Jaime Spacco <jspacco@knox.edu>
// Copyright (C) 2011-2015, David H. Hovemeyer <david.hovemeyer@gmail.com>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program 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 Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

package org.cloudcoder.dataanalysis;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.StringWriter;
import java.io.Writer;
import java.nio.charset.Charset;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Scanner;

import org.apache.commons.io.IOUtils;
import org.cloudcoder.app.server.persist.Database;
import org.cloudcoder.app.server.persist.IDatabase;
import org.cloudcoder.app.shared.model.Course;
import org.cloudcoder.app.shared.model.CourseRegistration;
import org.cloudcoder.app.shared.model.CourseRegistrationList;
import org.cloudcoder.app.shared.model.Problem;
import org.cloudcoder.app.shared.model.ProblemList;
import org.cloudcoder.app.shared.model.TestCase;
import org.cloudcoder.app.shared.model.User;

import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonGenerator;

/**
 * Export data about a CloudCoder {@link Course} in
 * <a href="http://cloudcoderdotorg.github.io/progsnap-spec/">progsnap</a>
 * format.
 * 
 * @author David Hovemeyer
 */
public class ProgsnapExport {
    // Version of progsnap spec the exported data will conform to
    private static final String PSVERSION = "0.0-dev";

    private File baseDir;
    private Properties config;
    private Map<String, Object> datasetProps;
    private int courseId;
    private String username;

    public ProgsnapExport() {

    }

    public void setBaseDir(File baseDir) {
        this.baseDir = baseDir;
    }

    public void setConfig(Properties config) {
        this.config = config;
    }

    public void setDatasetProps(Map<String, Object> datasetProps) {
        this.datasetProps = datasetProps;
    }

    public void setCourseId(int courseId) {
        this.courseId = courseId;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public void execute() throws IOException {
        Util.connectToDatabase(config);

        User user = findUser(username);
        Course course = findCourse(user, courseId);

        // Ensure that output directory exists
        baseDir.mkdirs();

        // Write dataset file
        try (Writer w = writeToFile(new File(baseDir, "/dataset.txt"))) {
            writeTaggedFile(w, datasetProps);
        }

        // Gather problems
        ProblemList problems = getProblems(user, course);

        // Write assignments file
        writeAssignmentsFile(problems);

        // Write assignment files
        for (Problem p : problems.getProblemList()) {
            writeAssignmentFile(p);
        }

        // Write students file
        List<User> users = getUsers(course);
        writeStudentsFile(users, course);
    }

    private void writeTaggedFile(Writer w, Map<String, Object> props) throws IOException {
        for (Map.Entry<String, Object> entry : props.entrySet()) {
            String tagname = entry.getKey();
            Object value = entry.getValue();

            String line = encodeLine(tagname, value);
            w.write(line);
            w.write("\n");
        }
    }

    // Encode a line consisting of a tagname and a value
    private String encodeLine(String tagname, Object value) throws IOException {
        StringWriter sw = new StringWriter();
        JsonFactory factory = new JsonFactory();
        JsonGenerator jg = factory.createGenerator(sw);
        jg.writeStartObject();
        jg.writeStringField("tag", tagname);
        jg.writeFieldName("value");
        writeJsonFieldValue(jg, value);
        jg.writeEndObject();
        jg.close();
        return sw.toString();
    }

    private Writer writeToFile(File out) throws FileNotFoundException {
        return new BufferedWriter(new OutputStreamWriter(new FileOutputStream(out), Charset.forName("UTF-8")));
    }

    private void writeJsonFieldValue(JsonGenerator jg, Object value) throws IOException {
        if (value instanceof String) {
            jg.writeString((String) value);
        } else if (value instanceof Integer) {
            jg.writeNumber(((Integer) value).intValue());
        } else if (value instanceof Long) {
            jg.writeNumber(((Long) value).longValue());
        } else if (value instanceof Double) {
            jg.writeNumber(((Double) value).doubleValue());
        } else if (value instanceof Map) {
            jg.writeStartObject();
            for (Map.Entry<?, ?> entry : ((Map<?, ?>) value).entrySet()) {
                jg.writeFieldName(entry.getKey().toString());
                writeJsonFieldValue(jg, entry.getValue());
            }
            jg.writeEndObject();
        } else if (value instanceof Boolean) {
            jg.writeBoolean(((Boolean) value).booleanValue());
        } else {
            throw new IllegalArgumentException(
                    "Don't know how to encode " + value.getClass().getSimpleName() + " as JSON value");
        }
    }

    private User findUser(String username) {
        IDatabase db = Database.getInstance();
        return db.getUserWithoutAuthentication(username);
    }

    private Course findCourse(User user, int courseId) {
        IDatabase db = Database.getInstance();
        List<? extends Object[]> courses = db.getCoursesForUser(user);
        for (Object[] triple : courses) {
            Course course = (Course) triple[0];
            if (course.getId() == courseId) {
                return course;
            }
        }
        throw new IllegalArgumentException("Could not find course " + courseId + " for user " + user.getUsername());
    }

    private ProblemList getProblems(User user, Course course) {
        IDatabase db = Database.getInstance();

        return db.getProblemsInCourse(user, course);
    }

    private void writeAssignmentsFile(ProblemList problems) throws IOException {
        Writer w = writeToFile(new File(baseDir, "/assignments.txt"));
        try {
            for (Problem p : problems.getProblemList()) {
                int problemId = p.getProblemId();
                Map<String, Object> obj = new LinkedHashMap<>();
                String path = String.format("/assignment_%04d.txt", problemId);
                obj.put("number", problemId);
                obj.put("path", path);
                String line = encodeLine("assignment", obj);
                w.write(line);
                w.write("\n");
            }
        } finally {
            IOUtils.closeQuietly(w);
        }
    }

    private void writeAssignmentFile(Problem p) throws IOException {
        IDatabase db = Database.getInstance();

        Writer w = writeToFile(new File(baseDir, String.format("/assignment_%04d.txt", p.getProblemId())));

        try {
            Map<String, Object> assignmentProps = new LinkedHashMap<>();
            // name
            // language
            // assigned
            // due
            assignmentProps.put("name", p.toNiceString());
            assignmentProps.put("language", p.getProblemType().getLanguage().getName());
            assignmentProps.put("assigned", p.getWhenAssigned());
            assignmentProps.put("due", p.getWhenDue());
            writeTaggedFile(w, assignmentProps);

            List<TestCase> tests = db.getTestCasesForProblem(p.getProblemId());
            int count = 0;
            for (TestCase t : tests) {
                Map<String, Object> test = new LinkedHashMap<>();
                // number
                // name
                // input
                // output
                // opaque
                // invisible
                test.put("number", count++);
                test.put("name", t.getTestCaseName());
                test.put("input", t.getInput());
                test.put("output", t.getOutput());
                test.put("opaque", t.isSecret());
                test.put("invisible", false);
                String line = encodeLine("test", test);
                w.write(line);
                w.write("\n");
            }
        } finally {
            IOUtils.closeQuietly(w);
        }
    }

    private List<User> getUsers(Course course) {
        IDatabase db = Database.getInstance();

        return db.getUsersInCourse(course.getId(), 0);
    }

    private void writeStudentsFile(List<User> users, Course course) throws IOException {
        IDatabase db = Database.getInstance();

        try (Writer w = writeToFile(new File(baseDir, "/students.txt"))) {
            for (User user : users) {
                CourseRegistrationList regList = db.findCourseRegistrations(user, course);
                // number
                // instructor
                // FIXME: allow loading of demographic information
                Map<String, Object> student = new LinkedHashMap<>();
                student.put("number", user.getId());
                student.put("instructor", regList.isInstructor());
                String line = encodeLine("student", student);
                w.write(line);
                w.write("\n");
            }
        }
    }

    public static void main(String[] args) throws IOException {
        ProgsnapExport exporter = new ProgsnapExport();

        @SuppressWarnings("resource")
        Scanner keyboard = new Scanner(System.in);

        boolean interactiveConfig = false;

        for (String arg : args) {
            if (arg.equals("--interactiveConfig")) {
                // Configure interactively rather than using embedded cloudcoder.properties
                interactiveConfig = true;
            } else {
                throw new IllegalArgumentException("Unknown option: " + arg);
            }
        }
        Util.configureLogging();
        Properties config = new Properties();
        if (interactiveConfig) {
            Util.readDatabaseProperties(keyboard, config);
        } else {
            Util.loadEmbeddedConfig(config, Retest.class.getClassLoader());
        }
        exporter.setConfig(config);

        int courseId = Integer.parseInt(Util.ask(keyboard, "Course id: "));
        exporter.setCourseId(courseId);

        String username = Util.ask(keyboard, "Instructor username: ");
        exporter.setUsername(username);

        File baseDir = new File(Util.ask(keyboard, "Output directory: "));
        exporter.setBaseDir(baseDir);

        System.out.println("Enter data set properties:");
        Map<String, Object> datasetProps = new LinkedHashMap<>();
        datasetProps.put("psversion", PSVERSION);
        datasetProps.put("name", Util.ask(keyboard, "Data set name: "));
        datasetProps.put("contact", Util.ask(keyboard, "Contact name: "));
        datasetProps.put("email", Util.ask(keyboard, "Contact email: "));
        datasetProps.put("courseurl", Util.ask(keyboard, "Course URL: "));
        exporter.setDatasetProps(datasetProps);

        exporter.execute();
    }
}