elw.web.AdminController.java Source code

Java tutorial

Introduction

Here is the source code for elw.web.AdminController.java

Source

/*
* ELW : e-learning workspace
* Copyright (C) 2010  Anton Kraievoy
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

package elw.web;

import com.google.common.base.Strings;
import elw.dao.Auth;
import elw.dao.Ctx;
import elw.dao.Queries;
import elw.dao.ctx.CtxSolution;
import elw.miniweb.Message;
import elw.miniweb.ViewJackson;
import elw.vo.*;
import elw.web.core.Core;
import elw.web.core.IndexRow;
import elw.web.core.LogFilter;
import elw.web.core.W;
import org.akraievoy.base.Parse;
import org.apache.commons.fileupload.FileUploadException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.*;

import static elw.ElwPackage.*;

@Controller
@RequestMapping("/a/**/*")
public class AdminController extends ControllerElw {
    private static final Logger log = LoggerFactory.getLogger(AdminController.class);

    private final Queries queries;

    public AdminController(Queries queries, Core core, ElwServerConfig elwServerConfig) {
        super(core, elwServerConfig);
        this.queries = queries;
    }

    protected HashMap<String, Object> auth(final HttpServletRequest req, final HttpServletResponse resp,
            final boolean page, final boolean verified) throws IOException {
        final HashMap<String, Object> model = super.auth(req, resp, page, verified);

        if (model == null) {
            return null;
        }

        model.put(R_CTX, Ctx.fromString(req.getParameter(R_CTX)).resolve(queries));

        return model;
    }

    @Override
    protected String extraAuthValidations(Auth auth) {
        if (!auth.isAdm()) {
            return "Admin Auth Required";
        }

        return null;
    }

    @RequestMapping(value = "logout", method = RequestMethod.GET)
    public ModelAndView do_logout(final HttpServletRequest req, final HttpServletResponse resp) throws IOException {
        req.getSession(true).invalidate();
        resp.sendRedirect("index");
        return null;
    }

    @RequestMapping(value = "SessionMessage", method = RequestMethod.GET)
    public ModelAndView do_SessionMessageGet(final HttpServletRequest req, final HttpServletResponse resp)
            throws IOException {
        return new ModelAndView(ViewJackson.data(Message.getMessages(req)));
    }

    @RequestMapping(value = "SessionMessage/{stamp}", method = RequestMethod.DELETE)
    public ModelAndView do_SessionMessageDelete(final HttpServletRequest req, final HttpServletResponse resp,
            @PathVariable("stamp") final String stamp) throws IOException {
        Message.delete(req, stamp);
        return null;
    }

    @RequestMapping(value = "index", method = RequestMethod.GET)
    public ModelAndView do_index(final HttpServletRequest req, final HttpServletResponse resp) throws IOException {
        final HashMap<String, Object> model = auth(req, resp, true, false);
        if (model == null) {
            return null;
        }

        return new ModelAndView("a/index", model);
    }

    @RequestMapping(value = "rest/index", method = RequestMethod.GET)
    public ModelAndView do_restIndex(final HttpServletRequest req, final HttpServletResponse resp)
            throws IOException {
        final HashMap<String, Object> model = auth(req, resp, false, false);
        if (model == null) {
            return null;
        }

        final List<Enrollment> enrolls = core.getQueries().enrollments();
        final List<IndexRow> indexData = core.index(enrolls);
        return new ModelAndView(ViewJackson.success(indexData));
    }

    @RequestMapping(value = "summary", method = RequestMethod.GET)
    public ModelAndView do_summary(final HttpServletRequest req, final HttpServletResponse resp)
            throws IOException {
        return wmECG(req, resp, true, false, new WebMethodCtx() {
            @Override
            public ModelAndView handleCtx() throws IOException {
                W.storeFilter(req, model);
                W.filterDefault(model, "f_mode", "a");
                final LogFilter filter = W.parseFilter(req);

                final SortedMap<String, Map<String, List<Solution>>> fileMetas = new TreeMap<String, Map<String, List<Solution>>>();
                final Map<String, Double> ctxToScore = new HashMap<String, Double>();

                core.tasksData(ctx, filter, true, fileMetas, ctxToScore, null);

                model.put("fileMetas", fileMetas);
                model.put("ctxToScore", ctxToScore);
                model.put("totalBudget", ctx.getEnr().cmpTotalBudget());

                return new ModelAndView("a/summary", model);
            }
        });
    }

    @RequestMapping(value = "log", method = RequestMethod.GET)
    public ModelAndView do_log(final HttpServletRequest req, final HttpServletResponse resp) throws IOException {
        return wmECG(req, resp, true, false, new WebMethodCtx() {
            public ModelAndView handleCtx() throws IOException {
                W.storeFilter(req, model);
                return new ModelAndView("a/log", model);
            }
        });
    }

    @RequestMapping(value = "rest/log", method = RequestMethod.GET)
    public ModelAndView do_restLog(final HttpServletRequest req, final HttpServletResponse resp)
            throws IOException {
        return wmECG(req, resp, false, false, new WebMethodCtx() {
            public ModelAndView handleCtx() throws IOException {
                final Format format = (Format) model.get(FormatTool.MODEL_KEY);
                final List<Object[]> logData = core.log(ctx, format, W.parseFilter(req), true);

                return new ModelAndView(ViewJackson.success(logData));
            }
        });
    }

    @RequestMapping(value = "tasks", method = RequestMethod.GET)
    public ModelAndView do_tasks(final HttpServletRequest req, final HttpServletResponse resp) throws IOException {
        return wmECG(req, resp, true, false, new WebMethodCtx() {
            public ModelAndView handleCtx() throws IOException {
                W.storeFilter(req, model);
                return new ModelAndView("a/tasks", model);
            }
        });
    }

    @RequestMapping(value = "rest/tasks", method = RequestMethod.GET)
    public ModelAndView do_restTasks(final HttpServletRequest req, final HttpServletResponse resp)
            throws IOException {
        return wmECG(req, resp, false, false, new WebMethodCtx() {
            public ModelAndView handleCtx() throws IOException {
                final Format format = (Format) model.get(FormatTool.MODEL_KEY);

                final List<Object[]> logData = core.tasks(ctx, W.parseFilter(req), format, true);

                return new ModelAndView(ViewJackson.success(logData));
            }
        });
    }

    @RequestMapping(value = "approve", method = RequestMethod.GET)
    public ModelAndView do_approve(final HttpServletRequest req, final HttpServletResponse resp)
            throws IOException {
        return wmScore(req, resp, true, false, new WebMethodScore() {
            public ModelAndView handleScore(HttpServletRequest req, HttpServletResponse resp, Ctx ctx,
                    FileSlot slot, Solution file, Long stamp, Map<String, Object> model) {
                final SortedMap<Long, Score> allScores = core.getQueries().scores(ctx, slot, file);

                final long stampEff = stamp == null ? allScores.lastKey() : stamp;
                final Score score = allScores.get(stampEff);

                model.put("stamp", stamp);
                model.put("score", score);
                model.put("slot", slot);
                model.put("file", file);

                return new ModelAndView("a/approve", model);
            }
        });
    }

    @RequestMapping(value = "rest/scoreLog", method = RequestMethod.GET)
    public ModelAndView do_restScoreLog(final HttpServletRequest req, final HttpServletResponse resp)
            throws IOException {
        return wmScore(req, resp, false, false, new WebMethodScore() {
            public ModelAndView handleScore(HttpServletRequest req, HttpServletResponse resp, Ctx ctx,
                    FileSlot slot, Solution file, Long stamp, Map<String, Object> model) {
                final Format f = (Format) model.get(FormatTool.MODEL_KEY);

                final SortedMap<Long, Score> allScores = core.getQueries().scores(ctx, slot, file);

                final String mode = req.getParameter("f_mode");

                final List<Object[]> logData = core.logScore(allScores, ctx, slot, file, f, mode, stamp);

                return new ModelAndView(ViewJackson.success(logData));
            }
        });
    }

    @RequestMapping(value = "approve", method = RequestMethod.POST)
    public ModelAndView do_approvePost(final HttpServletRequest req, final HttpServletResponse resp)
            throws IOException {
        return wmScore(req, resp, false, true, new WebMethodScore() {
            public ModelAndView handleScore(HttpServletRequest req, HttpServletResponse resp, Ctx ctx,
                    FileSlot slot, Solution file, Long stamp, Map<String, Object> model) throws IOException {
                final CtxSolution ctxSolution = ctx.ctxSlot(slot).solution(file);

                final long stampEff = stamp == null ? Long.MAX_VALUE : stamp;

                final Score score = core.getQueries().score(ctxSolution, stampEff);

                final String action = Strings.nullToEmpty(req.getParameter("action"));
                final List<String> validActions = Arrays.asList("next", "approve", "approve_clean", "decline");
                if (!validActions.contains(action.toLowerCase())) {
                    resp.sendError(HttpServletResponse.SC_BAD_REQUEST, "Bad action: " + action);
                    return null;
                }

                if ("approve".equalsIgnoreCase(action) || "approve_clean".equalsIgnoreCase(action)
                        || "decline".equalsIgnoreCase(action)) {
                    score.setApproved(
                            "approve".equalsIgnoreCase(action) || "approve_clean".equalsIgnoreCase(action));

                    final Map<String, Integer> pows = new TreeMap<String, Integer>(score.getPows());
                    final Map<String, Double> ratios = new TreeMap<String, Double>(score.getRatios());

                    for (Criteria cri : slot.getCriterias().values()) {
                        final int powDef = score.getPow(slot, cri);
                        final double ratioDef = score.getRatio(slot, cri);

                        final String idFor = Score.idFor(slot, cri);
                        final String powReq = req.getParameter(idFor);
                        final String ratioReq = req.getParameter(idFor + "--ratio");

                        pows.put(idFor, Parse.oneInt(powReq, powDef));
                        ratios.put(idFor, Parse.oneDouble(ratioReq, ratioDef));
                    }

                    score.setPows(pows);
                    score.setRatios(ratios);
                    score.setComment(req.getParameter("comment"));
                    queries.createScore(ctxSolution, score);
                }

                if ("approve_clean".equalsIgnoreCase(action)) {
                    final List<Solution> dupes = queries.solutions(ctxSolution);
                    for (Solution dupe : dupes) {
                        if (dupe.getStamp().equals(ctxSolution.solution.getStamp())) {
                            continue;
                        }
                        if (dupe.getId().equals(ctxSolution.solution.getId())) {
                            //  separate fix for name-id collision:
                            //      uploads with the same name are treated as same entity
                            //      when navigating from solution to score
                            continue;
                        }

                        final CtxSolution ctxDupe = ctxSolution.solution(dupe);
                        final Score scoreDupe = core.getQueries().score(ctxDupe, Long.MAX_VALUE);

                        scoreDupe.setApproved(Boolean.FALSE);
                        score.setComment("Duplicate");

                        queries.createScore(ctxDupe, scoreDupe);
                    }

                }

                resp.sendRedirect(core.cmpForwardToEarliestPendingSince(ctx, slot, file.getStamp()));

                return null;
            }
        });
    }

    @RequestMapping(value = "groups", method = RequestMethod.GET)
    public ModelAndView do_groups(final HttpServletRequest req, final HttpServletResponse resp) throws IOException {
        final HashMap<String, Object> model = auth(req, resp, true, false);
        if (model == null) {
            return null;
        }

        final List<Group> groups = core.getQueries().groups();

        model.put("groups", groups);

        return new ModelAndView("a/groups", model);
    }

    @RequestMapping(value = "ul", method = { RequestMethod.GET })
    public ModelAndView do_ul(final HttpServletRequest req, final HttpServletResponse resp)
            throws IOException, FileUploadException {
        return wmFile(req, resp, null, true, false, new WebMethodFile() {
            @Override
            protected ModelAndView handleFile(String scope, FileSlot slot) throws IOException {
                model.put("slot", slot);
                final Ctx ctx = (Ctx) model.get("elw_ctx");
                model.put("elw_ctx_type",
                        Ctx.forEnr(ctx.getEnr()).extCourse(ctx.getCourse()).extIndexEntry(ctx.getIndexEntry()));
                return new ModelAndView("a/ul", model);
            }
        });
    }

    @RequestMapping(value = "ul", method = RequestMethod.POST)
    public ModelAndView do_ulPost(final HttpServletRequest req, final HttpServletResponse resp)
            throws IOException, FileUploadException {
        return wmFile(req, resp, null, false, true, new WebMethodFile() {
            @Override
            protected ModelAndView handleFile(String scope, FileSlot slot) throws IOException {
                final String failureUri = core.getUri().upload(ctx, scope, slot.getId());
                final String refreshUri = core.getUri().logCourseE(ctx.getEnr().getId());
                final String authorName = auth(model).getAdmin().getName();

                return storeFile(slot, refreshUri, failureUri, authorName, core.getQueries(), new Attachment());
            }
        });
    }

    @RequestMapping(value = "dl/*.*", method = RequestMethod.GET)
    public ModelAndView do_dl(final HttpServletRequest req, final HttpServletResponse resp) throws IOException {
        return wmFile(req, resp, null, false, true, new WebMethodFile() {
            @Override
            protected ModelAndView handleFile(String scope, FileSlot slot) throws IOException {
                final String fileId = req.getParameter("fId");
                if (fileId == null || fileId.trim().length() == 0) {
                    resp.sendError(HttpServletResponse.SC_BAD_REQUEST, "no fileId (fId) defined");
                    return null;
                }

                final FileBase entry = core.getQueries().file(scope, ctx, slot, fileId);
                if (entry == null) {
                    resp.sendError(HttpServletResponse.SC_NOT_FOUND, "no file found");
                    return null;
                }

                retrieveFile(entry, slot, core.getQueries());

                return null;
            }
        });
    }

    @RequestMapping(value = "SelftestStatus", method = RequestMethod.GET)
    public ModelAndView do_selftestStatus(final HttpServletRequest req, final HttpServletResponse resp)
            throws IOException {
        final List<Message> messages = new ArrayList<Message>();

        queries.invalidateCaches();

        final List<Admin> admins = queries.admins();
        for (Admin a : admins) {
            if (a.getOpenIds().isEmpty()) {
                messages.add(Message.warn("no openIds set for " + a));
            }
        }
        for (List<Admin> dupes : filterDuplicateIds(admins)) {
            messages.add(Message.err("Admin records with duplicate ids: " + mapToCouchIds(dupes)));
        }

        //  FIXME there's a GRAND MESS below this innocent method:
        //      aliased data objects updates under a global lock when the objects already sit in the cache
        final List<Course> courses = queries.courses();
        for (Course c : courses) {
            //  FIXME this place is really unsafe, but I don't have access to raw data to complain properly
            //      actually the GRAND MESS was caused by someone trying to prevent me from having the access
        }
        for (List<Course> dupes : filterDuplicateIds(courses)) {
            messages.add(Message.err("Course records with duplicate ids: " + mapToCouchIds(dupes)));
        }
        final Map<String, Course> coursesById = groupById(courses);

        final List<Group> groups = queries.groups();
        for (Group g : groups) {
            for (Student s : g.getStudents().values()) {
                if (s.getEmail() == null || s.getEmail().isEmpty()) {
                    messages.add(Message.warn(String.format("No email set for student %s(%s) of group %s",
                            s.getName(), s.getId(), g.getId())));
                }
            }
            //  LATER check that all students have unique emails and names
        }
        for (List<Group> dupes : filterDuplicateIds(groups)) {
            messages.add(Message.err("Group records with duplicate ids: " + mapToCouchIds(dupes)));
        }
        final Map<String, Group> groupsById = groupById(groups);

        final List<Enrollment> enrollments = queries.enrollments();
        for (Enrollment e : enrollments) {
            if (!coursesById.containsKey(e.getCourseId())) {
                messages.add(Message.err(String.format("Enrollment %s refers to missing course %s", e.getCouchId(),
                        e.getCourseId())));
            }
            if (!groupsById.containsKey(e.getGroupId())) {
                messages.add(Message.err(
                        String.format("Enrollment %s refers to missing group %s", e.getCouchId(), e.getGroupId())));
            }
        }
        for (List<Enrollment> dupes : filterDuplicateIds(enrollments)) {
            messages.add(Message.err("Enrollment records with duplicate ids: " + mapToCouchIds(dupes)));
        }

        return new ModelAndView(ViewJackson.data(messages));
    }
}