package net.nationstatesplusplus.assembly; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.HashSet; import java.util.regex.Matcher; import net.nationstatesplusplus.assembly.util.DatabaseAccess; import org.apache.commons.dbutils.DbUtils; import org.joda.time.Duration; import play.Logger; import com.limewoodMedia.nsapi.NationStates; import com.limewoodMedia.nsapi.enums.WACouncil; import com.limewoodMedia.nsapi.holders.WAData; import com.limewoodMedia.nsapi.holders.WAResolution; public class WorldAssemblyTask implements Runnable { private final static WACouncil[] WA_COUNCILS = { WACouncil.GENERAL_ASSEMBLY, WACouncil.SECURITY_COUNCIL }; private final DatabaseAccess database; private final NationStates api; private final int council; private boolean firstRun = true; public WorldAssemblyTask(DatabaseAccess database, NationStates api, int council) { this.database = database; this.api = api; this.council = council; } private long firstRun() throws SQLException { WAData data = api.getWAInfo(WA_COUNCILS[council], WAData.Shards.RESOLUTION); WAResolution res = data.resolution; Connection conn = null; try { conn = database.getPool().getConnection(); PreparedStatement select = conn.prepareStatement("SELECT created FROM assembly.wa_resolutions WHERE created = ? AND council = ?"); select.setLong(1, res.created * 1000L); select.setInt(2, council); ResultSet result = select.executeQuery(); if (!result.next()) { insertWAResolution(res, conn); } DbUtils.closeQuietly(result); DbUtils.closeQuietly(select); int resId = getResolutionId(conn, res.created * 1000L); getLastWAVoteTime(conn, resId); long lastUpdate = getLastWAVoteTime(conn, resId); if (lastUpdate != -1) { return ((lastUpdate / 1000L) - res.created) % 3600; } return 1; } finally { DbUtils.closeQuietly(conn); } } private void insertWAResolution(WAResolution res, Connection conn) throws SQLException { if (res.category != null) { PreparedStatement insert = conn.prepareStatement("INSERT INTO assembly.wa_resolutions (category, created, wa_resolutions.desc, name, proposer, council) VALUES (?, ?, ?, ?, ?, ?)"); insert.setString(1, res.category); insert.setLong(2, res.created * 1000L); insert.setString(3, res.description); insert.setString(4, res.name); insert.setString(5, res.proposedBy); insert.setInt(6, council); insert.executeUpdate(); DbUtils.closeQuietly(insert); } } private void runSafe() throws Exception { if (firstRun) { Logger.info("Executing first WA Update for council: " + council); long nextUpdate = firstRun(); firstRun = false; Logger.info("First WA run update complete, next update in " + nextUpdate + " seconds"); RepeatingTaskThread thread = new RepeatingTaskThread(Duration.standardSeconds(nextUpdate), null, this); thread.start(); } else { Logger.info("Executing WA Update for council: " + council); RepeatingTaskThread thread = new RepeatingTaskThread(Duration.standardHours(1), null, this); thread.start(); updateVotes(); } } private long getLastWAVoteTime(Connection conn, int resolution) throws SQLException { PreparedStatement select = null; ResultSet result = null; try { select = conn.prepareStatement("SELECT timestamp FROM assembly.wa_votes WHERE wa_resolution = ? ORDER BY timestamp DESC LIMIT 0, 1"); select.setInt(1, resolution); result = select.executeQuery(); if (result.next()) return result.getLong(1); } finally { DbUtils.closeQuietly(result); DbUtils.closeQuietly(select); } return -1; } private int getResolutionId(Connection conn, long created) throws SQLException { PreparedStatement select = null; ResultSet result = null; try { select = conn.prepareStatement("SELECT id FROM assembly.wa_resolutions WHERE created = ? AND council = ?"); select.setLong(1, created); select.setInt(2, council); result = select.executeQuery(); if (result.next()) return result.getInt(1); } finally { DbUtils.closeQuietly(result); DbUtils.closeQuietly(select); } return -1; } private void updateVotes() throws SQLException { WAData data = api.getWAInfo(WA_COUNCILS[council], WAData.Shards.RESOLUTION); WAResolution res = data.resolution; if (data.resolution.category == null) { return; } Connection conn = null; try { conn = database.getPool().getConnection(); HashSet<Integer> votesFor = new HashSet<Integer>(); HashSet<Integer> votesAgainst = new HashSet<Integer>(); PreparedStatement select = conn.prepareStatement("SELECT nation, type FROM assembly.global_happenings WHERE timestamp > ? AND (type = 20 OR type = 21) AND happening LIKE ? ORDER BY timestamp ASC"); select.setLong(1, res.created * 1000L); select.setString(2, "%" + res.name.replaceAll(String.valueOf('"'), Matcher.quoteReplacement(""")).replaceAll("'", Matcher.quoteReplacement("\\'")) + "%"); ResultSet votes = select.executeQuery(); while(votes.next()) { boolean voteFor = votes.getInt(2) == 20; int nation = votes.getInt(1); if (voteFor) { votesFor.add(nation); votesAgainst.remove(nation); } else { votesAgainst.add(nation); votesFor.remove(nation); } } int resId = getResolutionId(conn, res.created * 1000L); if (resId == -1) { insertWAResolution(res, conn); resId = getResolutionId(conn, res.created * 1000L); } if (resId == -1) { Logger.error("Unknown WA Resolution: " + res.name); throw new IllegalStateException("Unknown WA Resolution: " + res.name); } PreparedStatement insert = conn.prepareStatement("INSERT INTO assembly.wa_votes (wa_resolution, delegate_votes_against, delegate_votes_for, nation_votes_against, nation_votes_for, timestamp) VALUES (?, ?, ?, ?, ?, ?)"); insert.setInt(1, resId); insert.setInt(2, res.votesAgainst); insert.setInt(3, res.votesFor); insert.setInt(4, votesAgainst.size()); insert.setInt(5, votesFor.size()); insert.setLong(6, System.currentTimeMillis()); insert.executeUpdate(); } finally { DbUtils.closeQuietly(conn); } } @Override public void run() { try { runSafe(); } catch (Exception e) { Logger.error("Exception handling World Assembly update", e); } } }