package edu.brown.cs.student.term; import com.google.common.collect.ImmutableMap; import edu.brown.cs.student.term.hub.Holder; import edu.brown.cs.student.term.hub.LinkMapper; import edu.brown.cs.student.term.profit.ProfitCalculation; import edu.brown.cs.student.term.profit.StockHolding; import edu.brown.cs.student.term.hub.SuspicionRanker; import edu.brown.cs.student.term.repl.Command; import edu.brown.cs.student.term.repl.REPL; import edu.brown.cs.student.term.repl.commands.LoadCommand; import edu.brown.cs.student.term.repl.commands.RankCommand; import edu.brown.cs.student.term.repl.commands.SetupCommand; import edu.brown.cs.student.term.trade.Trade; import joptsimple.OptionParser; import joptsimple.OptionSet; import java.io.OutputStream; import java.io.PrintStream; import java.time.Instant; import java.sql.Date; import java.util.HashMap; import spark.*; import spark.template.freemarker.FreeMarkerEngine; import freemarker.template.Configuration; //fix import java.io.File; import java.io.IOException; import java.io.PrintWriter; import java.io.StringWriter; import java.util.LinkedList; import java.util.List; import java.util.Map; import com.google.gson.Gson; import org.json.JSONObject; /** * The Main class of our project. This is where execution begins. */ public final class Main { public static JSONObject xmlLinks = null; private static final Gson GSON = new Gson(); private static final int DEFAULT_PORT = 4567; /** * The initial method called when execution begins. * * @param args An array of command line arguments */ public static void main(String[] args) { new Main(args).run(); } private String[] args; private Main(String[] args) { this.args = args; } private void run() { // Parse command line arguments OptionParser parser = new OptionParser(); parser.accepts("gui"); parser.accepts("port").withRequiredArg().ofType(Integer.class) .defaultsTo(DEFAULT_PORT); parser.accepts("debug"); OptionSet options = parser.parse(args); if (options.has("gui")) { runSparkServer((int) options.valueOf("port")); //will auto connect to correct db when running gui! SetupCommand setConnection = new SetupCommand(); setConnection.run(new String[] {"data/trades.sqlite3"}); } /*if (!options.has("debug")) { System.setErr(new PrintStream(new OutputStream() { public void write(int b) { } })); }*/ HashMap commandHashMap = new HashMap<>(); commandHashMap.put("setup", new SetupCommand()); commandHashMap.put("load", new LoadCommand()); commandHashMap.put("rank", new RankCommand()); /** add commands to map here! */ REPL repl = new REPL(commandHashMap); repl.runREPL(); } private static FreeMarkerEngine createEngine() { Configuration config = new Configuration(); File templates = new File("src/main/resources/spark/template/freemarker"); try { config.setDirectoryForTemplateLoading(templates); } catch (IOException ioe) { System.out.printf("ERROR: Unable use %s for template loading.%n", templates); System.exit(1); } return new FreeMarkerEngine(config); } public void runSparkServer(int port) { Spark.port(port); Spark.externalStaticFileLocation("src/main/resources/static"); Spark.exception(Exception.class, new ExceptionPrinter()); Spark.options("/*", (request, response) -> { String accessControlRequestHeaders = request .headers("Access-Control-Request-Headers"); if (accessControlRequestHeaders != null) { response.header("Access-Control-Allow-Headers", accessControlRequestHeaders); } String accessControlRequestMethod = request .headers("Access-Control-Request-Method"); if (accessControlRequestMethod != null) { response.header("Access-Control-Allow-Methods", accessControlRequestMethod); } return "OK"; }); Spark.before((request, response) -> response.header("Access-Control-Allow-Origin", "*")); //TODO: Add system testing for all of our end points Spark.post("/data", new SuspicionRankHandler()); Spark.post("/profit", new ProfitQueryHandler()); Spark.post("/trade-lookup", new TradeQueryHandler()); Spark.post("/edge-data", new EdgeDataQueryHandler()); } /** * Gets the list of holders with id, name, and suspicion rank. */ private static class SuspicionRankHandler implements Route { @Override /** * Expects that the request will contain two longs that are the start/end * dates for the suspicion rank to run on as epoch time in milliseconds */ public Object handle(Request request, Response response) throws Exception { //String str = request.body(); //xmlLinks = new JSONObject(str); //this is all the filedAt times and xml files try { System.err.println("LOG: Call to /data from frontend"); DatabaseQuerier db = SetupCommand.getDq(); SuspicionRanker ranker = new SuspicionRanker(db); JSONObject data = new JSONObject(request.body()); long startMilli = data.getLong("start"); long endMilli = data.getLong("end"); Instant start = Instant.ofEpochMilli(startMilli); Instant end = Instant.ofEpochMilli(endMilli); System.err.println("LOG: Call to ranker.getSusscore in " + getClass()); List suspiciousHolders = ranker.getSuspicionScoreList(start, end); System.err.println("LOG: Making map " + getClass()); Map variables = ImmutableMap.of("holders", suspiciousHolders); return GSON.toJson(variables); } catch (Exception e) { System.out.println("Error retrieving the suspicion ranks for GUI"); return "Error"; } } } private static class ProfitQueryHandler implements Route { @Override public Object handle(Request request, Response response) throws Exception { JSONObject req = new JSONObject(request.body()); System.err.println("LOG: Call to /profit with " + req.toMap()); Integer holder_id = req.getInt("selectedId"); Date startPeriod = new Date(req.getLong("startTime")); Date endPeriod = new Date(req.getLong("endTime")); ProfitCalculation profit = new ProfitCalculation(DatabaseQuerier.getConn(), "", startPeriod, endPeriod); List holdings = profit.getHoldingsList(holder_id); double gains = profit.calculateGainsSingle(holder_id); double sp500PercentGain = profit.compareToSP500(); double percentGain = 100 * (gains / profit.getMoneyInput()); if (profit.getMoneyInput() == 0) { percentGain = 0; } Map res = new HashMap<>(); res.put("holder_id", holder_id); res.put("moneyIn", profit.getMoneyInput()); res.put("moneyOut", profit.getMoneyInput() + gains); res.put("holdings", holdings); res.put("percentGain", percentGain); res.put("SP500", (1 + sp500PercentGain) * profit.getMoneyInput()); res.put("percentSP500", 100 * sp500PercentGain); System.err.println("LOG: Returning to GUI " + res); return GSON.toJson(res); } } private static class TradeQueryHandler implements Route { @Override public Object handle(Request request, Response response) throws Exception { JSONObject req = new JSONObject(request.body()); Integer holder_id = req.getInt("selectedId"); Date startPeriod = new Date(req.getLong("startTime")); Date endPeriod = new Date(req.getLong("endTime")); DatabaseQuerier db = SetupCommand.getDq(); List trades = db.getAllTradesByHolder(holder_id, startPeriod, endPeriod); return GSON.toJson(trades); } } private static class EdgeDataQueryHandler implements Route { @Override public Object handle(Request request, Response response) throws Exception { JSONObject req = new JSONObject(request.body()); int leaderID = req.getInt("leaderID"); int followerID = req.getInt("followerID"); List commonStocks = LinkMapper.getCommonTrades(leaderID, followerID); System.out.println(commonStocks); return GSON.toJson(commonStocks); } } /** * Display an error page when an exception occurs in the server. */ private static class ExceptionPrinter implements ExceptionHandler { @Override public void handle(Exception e, Request req, Response res) { res.status(500); StringWriter stacktrace = new StringWriter(); try (PrintWriter pw = new PrintWriter(stacktrace)) { pw.println("
");
        e.printStackTrace(pw);
        pw.println("
"); } res.body(stacktrace.toString()); } } }