diff options
Diffstat (limited to 'src')
8 files changed, 627 insertions, 0 deletions
diff --git a/src/main/java/edu/brown/cs/student/term/parsing/LocalXmlParser.java b/src/main/java/edu/brown/cs/student/term/parsing/LocalXmlParser.java new file mode 100644 index 0000000..27c3988 --- /dev/null +++ b/src/main/java/edu/brown/cs/student/term/parsing/LocalXmlParser.java @@ -0,0 +1,38 @@ +package edu.brown.cs.student.term.parsing; + +import org.w3c.dom.Document; +import org.xml.sax.SAXException; + +import java.io.File; +import java.io.IOException; + +public class LocalXmlParser extends XmlParser { + public LocalXmlParser() { + super(); + } + + /** + * Method used to parse the xml file. + * + * @param pathToXml The path to the xml text file. + * @return The tree structure parsed as an xml doc. + */ + @Override + public Document parse(String pathToXml) { + // TODO: change to online hosted file option + // Creating the file reference. + System.err.println("LOG: To make file reference for " + pathToXml + " in " + getClass()); + File file = new File(pathToXml); + + // Parsing the file. + try { + System.err.println("LOG: Calling builder.parse() in " + getClass()); + return builder.parse(file); + } catch (SAXException e) { + System.err.println("INTERNAL: SAX " + getClass() + " : " + e.getClass()); + } catch (IOException e) { + System.err.println("INTERNAL: IO " + getClass() + " : " + e.getClass()); + } + return null; + } +} diff --git a/src/main/java/edu/brown/cs/student/term/parsing/Transaction.java b/src/main/java/edu/brown/cs/student/term/parsing/Transaction.java new file mode 100644 index 0000000..2111048 --- /dev/null +++ b/src/main/java/edu/brown/cs/student/term/parsing/Transaction.java @@ -0,0 +1,171 @@ +package edu.brown.cs.student.term.parsing; + +import edu.brown.cs.student.term.hub.Holder; +import edu.brown.cs.student.term.trade.Trade; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +import java.util.ArrayList; +import java.util.List; + + +/** + * This class represents the transaction data from the xml file. + */ +public class Transaction { + private Document doc; + private final List<Trade> trades; + private final String personName; + private final int id; + private final String ticker; + + /** + * Constructor that represents the transaction from the document. + * @param document The document parsed from the xml file. + */ + public Transaction(Document document) { + // TODO: add log comments + doc = document; + trades = new ArrayList<>(); + + personName = personName(); + id = id(); + ticker = ticker(); + + // There are two types of transactions within the xml - derivative and non derivative. + NodeList nonDerivative = document.getElementsByTagName("nonDerivativeTransaction"); + //NodeList derivative = document.getElementsByTagName("derivativeTransaction"); + // Processing both of their trades into the trades instance var. + processTransactions(nonDerivative); + //processTransactions(derivative); + } + + /** + * Takes a transaction as a node list, then processes and stores them into trades. + * @param tradesAsNodes The trades within the transaction as a nodelist. + */ + private void processTransactions(NodeList tradesAsNodes) { + int numTrades = tradesAsNodes.getLength(); + for(int i = 0; i < numTrades; i++) { + NodeList tradeValues = getValueList(tradesAsNodes.item(i)); + + // This stages the params for the trade. + // TODO: update with real timestamp + double ts = getDate(tradeValues).hashCode(); + int isBuy = getIsBuy(tradeValues) ? 1 : 0; + int numShares = getNumShares(tradeValues); + // TODO: update id? unique for each holder? + Holder holder = new Holder(id, personName); + + Trade trade = new Trade(id, ticker, ts, isBuy, numShares, holder); + trades.add(trade); + } + + doc = null; + } + + /** + * Extracts the id of the transaction from the document. + * @return The id. + */ + private int id() { + // Id of transaction + NodeList idNode = doc.getElementsByTagName("rptOwnerCik"); + // TODO: add error parsing + return Integer.parseInt(idNode.item(0).getTextContent()); + } + + /** + * Extracts the person's name of the transaction from the document. + * @return The person's name. + */ + private String personName() { + // Name of person + NodeList nameNode = doc.getElementsByTagName("rptOwnerName"); + return nameNode.item(0).getTextContent(); + } + + /** + * Extracts the security ticker of the transaction from the document + * @return The ticker. + */ + private String ticker() { + // Ticker of security + NodeList idNode = doc.getElementsByTagName("issuerTradingSymbol"); + return idNode.item(0).getTextContent(); + } + + /** + * Extracts the trade data of one trade within a transaction. + * @param trade One trade from the transaction as a node. + * @return A nodelist with each element holding specific info of the trade. + */ + public NodeList getValueList(Node trade) { + // Data of trade in an array of values + assert trade.getNodeType() == Node.ELEMENT_NODE; + Element tradeElement = (Element) trade; + return tradeElement.getElementsByTagName("value"); + } + + /** + * Extracts the trade date from a trade node. + * @param values The value array representing the trade. + * @return The date in mm-dd-yyyy format as a string. + */ + public String getDate(NodeList values) { + return values.item(1).getTextContent(); + } + + /** + * Extracts the number of shares moved from the trade. + * @param values The value array representing the trade. + * @return The number of shares. + */ + public int getNumShares(NodeList values) { + return Integer.parseInt(values.item(2).getTextContent()); + } + + /** + * Extracts the price each share cost from the trade. + * @param values The value array representing the trade. + * @return The price of each share. + */ + public int getPriceShares(NodeList values) { + return Integer.parseInt(values.item(3).getTextContent()); + } + + /** + * Determines whether the shares in the trade were bought or sold. + * @param values The value array representing the trade. + * @return True if they were bought (acquired). False, if sold. + */ + public boolean getIsBuy(NodeList values) { + return values.item(4).getTextContent().equals("A"); + } + + /** + * Returns the list of trades that occurred within the transaction. + * @return A list of trade objects. + */ + public List<Trade> getTrades() { + return trades; + } + + /** + * Accessor method that returns the id of the transaction. + * @return The id. + */ + public int getId() { + return id; + } + + /** + * Accessor method that returns the person's name who did the transaction. + * @return The name of whom did the transaction. + */ + public String getPersonName() { + return personName; + } +} diff --git a/src/main/java/edu/brown/cs/student/term/parsing/UrlXmlParser.java b/src/main/java/edu/brown/cs/student/term/parsing/UrlXmlParser.java new file mode 100644 index 0000000..440b898 --- /dev/null +++ b/src/main/java/edu/brown/cs/student/term/parsing/UrlXmlParser.java @@ -0,0 +1,37 @@ +package edu.brown.cs.student.term.parsing; + +import org.w3c.dom.Document; +import org.xml.sax.SAXException; + +import java.io.IOException; +import java.net.URL; +import java.net.URLConnection; + +public class UrlXmlParser extends XmlParser{ + public UrlXmlParser() { + super(); + } + + /** + * Method used to parse the xml file. + * + * @param pathToXml The path to the xml text file. + * @return The tree structure parsed as an xml doc. + */ + @Override + public Document parse(String pathToXml) { + try { + System.err.println("LOG: To make url class in parse() of " + getClass()); + URL url = new URL(pathToXml); + System.err.println("LOG: To establish urlConnection in parse() of " + getClass()); + URLConnection conn = url.openConnection(); + System.err.println("LOG: Calling builder.parse() in " + getClass()); + return builder.parse(conn.getInputStream()); + } catch (SAXException e) { + System.err.println("INTERNAL: SAX " + getClass() + " : " + e.getClass()); + } catch (IOException e) { + System.err.println("INTERNAL: IO " + getClass() + " : " + e.getClass()); + } + return null; + } +} diff --git a/src/main/java/edu/brown/cs/student/term/parsing/XmlParser.java b/src/main/java/edu/brown/cs/student/term/parsing/XmlParser.java new file mode 100644 index 0000000..d8182d6 --- /dev/null +++ b/src/main/java/edu/brown/cs/student/term/parsing/XmlParser.java @@ -0,0 +1,37 @@ +package edu.brown.cs.student.term.parsing; + +import org.w3c.dom.Document; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; + +public abstract class XmlParser { + protected DocumentBuilder builder = null; + + /** + * This constructor crates and saves the builder that turns the xml text into a tree stricture. + */ + protected XmlParser() { + // Builds the immutable factory + System.err.println("LOG: Constructor of " + getClass() + ". To make XML parser factory."); + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + factory.setValidating(true); + factory.setIgnoringElementContentWhitespace(true); + + // Creates the builder from the factory + try { + System.err.println("LOG: To make documentBuilder in " + getClass()); + builder = factory.newDocumentBuilder(); + } catch (ParserConfigurationException e) { + System.err.println("INTERNAL: " + getClass() + " : " + e.getClass()); + } + } + + /** + * Method used to parse the xml file. + * @param pathToXml The path to the xml text file. + * @return The tree structure parsed as an xml doc. + */ + public abstract Document parse(String pathToXml); +} diff --git a/src/main/java/edu/brown/cs/student/term/repl/commands/LoadCommand.java b/src/main/java/edu/brown/cs/student/term/repl/commands/LoadCommand.java new file mode 100644 index 0000000..a20c23b --- /dev/null +++ b/src/main/java/edu/brown/cs/student/term/repl/commands/LoadCommand.java @@ -0,0 +1,76 @@ +package edu.brown.cs.student.term.repl.commands; + +import edu.brown.cs.student.term.DatabaseQuerier; +import edu.brown.cs.student.term.parsing.Transaction; +import edu.brown.cs.student.term.parsing.UrlXmlParser; +import edu.brown.cs.student.term.repl.Command; +import edu.brown.cs.student.term.trade.Trade; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.SQLException; + +public class LoadCommand implements Command { + private final static Connection CONN = DatabaseQuerier.getConn(); + private final static UrlXmlParser URL_XML_PARSER = new UrlXmlParser(); + + /** + * Main run method for every command. + * + * @param args arguments for the command + */ + @Override + public String run(String[] args) { + // TODO: add log comments + System.err.println("LOG: Entered .run() of " + getClass()); + // TODO: call to api for urls to call through the urlxmlparser from reagan + String[] urls = new String[1]; + for (String url : urls) { + try { + System.err.println("LOG: Calling loadTransactionIntoDB() in " + getClass()); + loadTransactionIntoDB(url); + } catch (SQLException throwables) { + System.err.println("INTERNAL: SQLException in .run() of " + getClass()); + //throwables.printStackTrace(); + } + } + return "Loaded?"; + } + + /** + * Loads a whole transaction, which can have multiple trades, into the DB. + * @param url The url to the public xml file. + * @throws SQLException If the prep statement fails or db doesn't exist, throws. + */ + private static void loadTransactionIntoDB(String url) throws SQLException { + System.err.println("LOG: Parsing XML into transaction in loadTransactionIntoDB(). URL: " + url); + Transaction helper = new Transaction(URL_XML_PARSER.parse(url)); + for(Trade trade : helper.getTrades()) { + System.err.println("LOG: Loading a trade into DB -> " + trade); + loadTradeIntoDB(trade); + System.err.println("LOG: Loaded that trade."); + } + } + + /** + * Loads one trade into the DB. + * @param trade The trade to be loaded. + * @throws SQLException If the prep statement fails or db doesn't exist, throws. + */ + private static void loadTradeIntoDB(Trade trade) throws SQLException { + PreparedStatement prep = CONN.prepareStatement( + "INSERT into trades (stock_name, holder_name, trade_timestamp, is_buy, " + + "number_of_shares, holder_id) " + + "VALUES (?, ?, ?, ?, ?, ?)"); + + prep.setString(1, trade.getStock()); + prep.setString(2, trade.getHolder().getName()); + // TODO: update with timestamp @julia + prep.setDouble(3, trade.getTimestamp()); + prep.setInt(4, trade.isBuy() ? 1 : 0); + prep.setInt(5, trade.getNumShares()); + prep.setInt(6, trade.getHolder().getId()); + + prep.execute(); + } +}
\ No newline at end of file diff --git a/src/main/java/edu/brown/cs/student/term/trade/Trade.java b/src/main/java/edu/brown/cs/student/term/trade/Trade.java index 13f7ae1..95770e4 100644 --- a/src/main/java/edu/brown/cs/student/term/trade/Trade.java +++ b/src/main/java/edu/brown/cs/student/term/trade/Trade.java @@ -44,6 +44,10 @@ public class Trade { return holder; } + public int getNumShares() { + return numShares; + } + @Override public String toString() { return "Trade{" + diff --git a/src/test/java/edu/brown/cs/student/TransactionTest.java b/src/test/java/edu/brown/cs/student/TransactionTest.java new file mode 100644 index 0000000..f9a00f7 --- /dev/null +++ b/src/test/java/edu/brown/cs/student/TransactionTest.java @@ -0,0 +1,96 @@ +package edu.brown.cs.student; + +import edu.brown.cs.student.term.parsing.LocalXmlParser; +import edu.brown.cs.student.term.parsing.Transaction; +import edu.brown.cs.student.term.parsing.XmlParser; +import edu.brown.cs.student.term.trade.Trade; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +import static org.junit.Assert.*; + +public class TransactionTest { + private XmlParser _xmlParser; + private Document _doc; + + @Before + public void setUp() { + _xmlParser = new LocalXmlParser(); + } + + @After + public void tearDown() { + _xmlParser = null; + } + + @Test + public void singleTrade(){ + setUp(); + Document doc = _xmlParser.parse("data/xml_single_trade_test.xml"); + assertNotEquals(doc, null); + + // One trades in transaction + Transaction transaction = new Transaction(doc); + assertEquals(transaction.getTrades().size(), 1); + + // TODO: add more qualities on trade to test... + Trade firstTrade = transaction.getTrades().get(0); + assertEquals(firstTrade.getNumShares(), 8236); + + tearDown(); + } + + @Test + public void multipleTrades(){ + setUp(); + Document doc = _xmlParser.parse("data/xml_multiple_trades_test.xml"); + assertNotEquals(doc, null); + + // Three trades in transaction + Transaction transaction = new Transaction(doc); + assertEquals(transaction.getTrades().size(), 3); + + Trade firstTrade = transaction.getTrades().get(0); + assertEquals(firstTrade.getNumShares(), 3000); + + Trade secondTrade = transaction.getTrades().get(1); + assertEquals(secondTrade.getNumShares(), 3000); + + Trade lastTrade = transaction.getTrades().get(2); + assertEquals(lastTrade.getNumShares(), 2000); + + tearDown(); + } + + /* + @Test + public void derivativeTransaction(){ + setUp(); + Document doc = _xmlParser.parse("data/xml_derivative_only_test.xml"); + assertNotEquals(doc, null); + + // One trades in transaction + Transaction transaction = new Transaction(doc); + assertEquals(transaction.getTrades().size(), 1); + + // TODO: add more qualities on trade to test... + Trade firstTrade = transaction.getTrades().get(0); + assertEquals(firstTrade.getNumShares(), 8236); + assertEquals(firstTrade.getHolder().getId(), 1463126); + + tearDown(); + } + */ + + @Test + public void noTrades(){ + setUp(); + // TODO: add case, but won't realistically come up + tearDown(); + } +} diff --git a/src/test/java/edu/brown/cs/student/XmlParserTest.java b/src/test/java/edu/brown/cs/student/XmlParserTest.java new file mode 100644 index 0000000..684452b --- /dev/null +++ b/src/test/java/edu/brown/cs/student/XmlParserTest.java @@ -0,0 +1,168 @@ +package edu.brown.cs.student; + +import edu.brown.cs.student.term.parsing.LocalXmlParser; +import edu.brown.cs.student.term.parsing.UrlXmlParser; +import edu.brown.cs.student.term.parsing.XmlParser; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +import javax.print.Doc; + +import static org.junit.Assert.*; + +public class XmlParserTest { + private XmlParser _localXmlParser, _urlXmlParser; + private Document _doc; + + @Before + public void setUp() { + _localXmlParser = new LocalXmlParser(); + _urlXmlParser = new UrlXmlParser(); + } + + @After + public void tearDown() { + _localXmlParser = null; + _urlXmlParser = null; + } + + @Test + public void parsesLocal(){ + setUp(); + Document doc = _localXmlParser.parse("data/xml_single_trade_test.xml"); + assertNotNull(doc); + + // Id of person + assertEquals(getIdFromDoc(doc), "0001561844"); + tearDown(); + } + + @Test + public void parsesUrl(){ + setUp(); + Document doc = + _urlXmlParser.parse("https://www.sec.gov/Archives/edgar/data/1517006/000110465921046242/tm2112036-4_4seq1.xml"); + assertNotNull(doc); + + // Id of person + assertEquals(getIdFromDoc(doc), "0001561844"); + tearDown(); + } + + public String getIdFromDoc(Document doc) { + // Id of person + NodeList idNode = doc.getElementsByTagName("rptOwnerCik"); + assertEquals(idNode.getLength(), 1); + return idNode.item(0).getTextContent(); + } + + @Test + public void urlSameAsLocal(){ + setUp(); + Document local = _localXmlParser.parse("data/xml_single_trade_test.xml"); + Document url = + _urlXmlParser.parse("https://www.sec.gov/Archives/edgar/data/1517006/000110465921046242/tm2112036-4_4seq1.xml"); + + assertEquals(getIdFromDoc(local), getIdFromDoc(url)); + tearDown(); + } + + @Test + public void noFileExists(){ + setUp(); + tearDown(); + } + + @Test + public void badXmlFormat(){ + setUp(); + Document doc = _localXmlParser.parse("data/bad.xml"); + assertNull(doc); + tearDown(); + } + + @Test + public void personDataParse(){ + setUp(); + Document doc = _localXmlParser.parse("data/xml_single_trade_test.xml"); + assertNotNull(doc); + + // Id of person + NodeList idNode = doc.getElementsByTagName("rptOwnerCik"); + assertEquals(idNode.getLength(), 1); + String id = idNode.item(0).getTextContent(); + assertEquals(id, "0001561844"); + + // Name of person + NodeList nameNode = doc.getElementsByTagName("rptOwnerName"); + assertEquals(nameNode.getLength(), 1); + String name = nameNode.item(0).getTextContent(); + assertEquals(name, "Levental Igor"); + + tearDown(); + } + + @Test + public void securityDataParse(){ + setUp(); + Document doc = _localXmlParser.parse("data/xml_single_trade_test.xml"); + assertNotNull(doc); + + // Ticker of security + NodeList idNode = doc.getElementsByTagName("issuerTradingSymbol"); + assertEquals(idNode.getLength(), 1); + String id = idNode.item(0).getTextContent(); + assertEquals(id, "GATO"); + + tearDown(); + } + + @Test + public void tradeDataParse(){ + setUp(); + Document doc = _localXmlParser.parse("data/xml_single_trade_test.xml"); + assertNotEquals(doc, null); + + // Data of trade in an array of values + NodeList trade = doc.getElementsByTagName("nonDerivativeTransaction"); + assertEquals(trade.item(0).getNodeType(), Node.ELEMENT_NODE); + Element tradeElement = (Element) trade.item(0); + NodeList values = tradeElement.getElementsByTagName("value"); + assertEquals(values.getLength(), 7); + + // type of stock + String stockType = values.item(0).getTextContent(); + assertEquals(stockType, "Common Stock"); + + // date + String date = values.item(1).getTextContent(); + assertEquals(date, "2021-03-31"); + + // # of shares + String numShares = values.item(2).getTextContent(); + assertEquals(numShares, "8236"); + + // price of shares + String priceShares = values.item(3).getTextContent(); + assertEquals(priceShares, "0"); + + // transaction type (A for acquire) + String transactionType = values.item(4).getTextContent(); + assertEquals(transactionType, "A"); + + // shared after transaction + String sharesAfter = values.item(5).getTextContent(); + assertEquals(sharesAfter, "10799"); + + // ownership type + String ownershipType = values.item(6).getTextContent(); + assertEquals(ownershipType, "D"); + + tearDown(); + } +} |