package com.sunlightlabs.congress.models;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class Bill implements Serializable {
private static final long serialVersionUID = 1L;
// basic
public String id, bill_type, chamber;
public int congress, number;
public String short_title, official_title;
public Date last_action_at, last_passage_vote_at;
public int cosponsors_count;
public Date introduced_on, house_passage_result_at, senate_passage_result_at;
public Date vetoed_at, house_override_result_at, senate_override_result_at;
public Date senate_cloture_result_at;
public Date awaiting_signature_since, enacted_at;
public boolean vetoed, awaiting_signature, enacted;
public String house_passage_result, senate_passage_result, house_override_result, senate_override_result;
public String senate_cloture_result;
// new history field: whether and when a bill became unusually active
public boolean active;
public Date active_at;
// sponsor
public Legislator sponsor;
// cosponsors
public List<Legislator> cosponsors;
// summary
public String summary;
// votes
public List<Bill.Vote> votes;
// actions
public List<Bill.Action> actions;
public Bill.Action lastAction;
// search result metadata (if coming from a search)
public SearchResult search;
// latest upcoming bill data
public List<UpcomingBill> upcoming;
// homepage URLs on services
public Map<String,String> urls;
// full text URLs (to GPO)
// valid keys: "html", "xml", "pdf"
public Map<String,String> versionUrls;
public static class Action implements Serializable {
private static final long serialVersionUID = 1L;
// required
public String type, text;
public Date acted_at;
// optional
public String chamber, result;
}
public static class Vote implements Serializable {
private static final long serialVersionUID = 1L;
//todo: rename passage_type->vote_type
public String result, text, how, passage_type, chamber, roll_id;
public Date voted_at;
}
public static String normalizeCode(String code) {
return code.toLowerCase(Locale.US).replaceAll("[^\\w\\d]", "").replace("con", "c").replace("joint", "j").replace(" ", "").replace(".", "");
}
public static boolean isCode(String code) {
return Pattern.compile("^(hr|hres|hjres|hconres|s|sres|sjres|sconres)(\\d+)$").matcher(code).matches();
}
public static String currentSession() {
int year = Calendar.getInstance().get(Calendar.YEAR);
return "" + (((year + 1901) / 2) - 894);
}
public static String matchText(String field) {
if (field.equals("text"))
return "text";
else if (field.equals("short_title"))
return "title";
else if (field.equals("popular_title"))
return "nickname";
else if (field.equals("official_title"))
return "official title";
else if (field.equals("summary"))
return "summary";
else if (field.equals("keywords"))
return "official keywords";
else
return "";
}
// from a highlight hash, return the field name with the highest priority
public static String matchField(Map<String,ArrayList<String>> highlight) {
String[] priorities = new String[] {"popular_title", "short_title", "official_title", "summary", "text", "keywords"};
String field = null;
for (int i=0; i<priorities.length; i++) {
if (highlight.containsKey(priorities[i])) {
field = priorities[i];
return field;
}
}
return field;
}
public static String formatCode(String bill_id) {
Pattern pattern = Pattern.compile("^([a-z]+)(\\d+)-\\d+$");
Matcher matcher = pattern.matcher(bill_id);
if (!matcher.find())
return bill_id;
String bill_type = matcher.group(1);
int number = Integer.valueOf(matcher.group(2));
return formatCode(bill_type, number);
}
// also expanded to handle amendment IDs
public static String formatCode(String bill_type, int number) {
if (bill_type.equals("hr"))
return "H.R. " + number;
else if (bill_type.equals("hres"))
return "H. Res. " + number;
else if (bill_type.equals("hjres"))
return "H.J. Res. " + number;
else if (bill_type.equals("hconres"))
return "H.Con. Res. " + number;
else if (bill_type.equals("s"))
return "S. " + number;
else if (bill_type.equals("sres"))
return "S. Res. " + number;
else if (bill_type.equals("sjres"))
return "S.J. Res. " + number;
else if (bill_type.equals("sconres"))
return "S.Con. Res. " + number;
else if (bill_type.equals("hamdt"))
return "H.Amdt. " + number;
else if (bill_type.equals("samdt"))
return "S.Amdt. " + number;
else
return bill_type + number;
}
// prioritizes GPO "html" version (really text),
// then GPO PDF, then finally the THOMAS landing page
public String bestFullTextUrl() {
if (this.versionUrls != null && this.versionUrls.containsKey("html"))
return versionUrls.get("html");
else if (this.versionUrls != null && this.versionUrls.containsKey("pdf"))
return versionUrls.get("pdf");
else if (this.urls != null && this.urls.containsKey("congress"))
return this.urls.get("congress");
else
return fallbackTextUrl();
}
// next best thing to official, easily calculable from bill fields
public String fallbackTextUrl() {
return "http://www.govtrack.us/congress/bills/" + congress + "/" + bill_type + number + "/text";
}
public String sunlightShortUrl() {
return "http://cngr.es/b/" + this.id;
}
public static String formatSummary(String summary, String short_title) {
String formatted = summary;
formatted = formatted.replaceFirst("^\\d+\\/\\d+\\/\\d+--.+?\\.\\s*", "");
formatted = formatted.replaceFirst("(\\(This measure.+?\\))\n*\\s*", "");
if (short_title != null)
formatted = formatted.replaceFirst("^" + short_title + " - ", "");
formatted = formatted.replaceAll("\n", "\n\n");
formatted = formatted.replaceAll(" (\\(\\d\\))", "\n\n$1");
formatted = formatted.replaceAll("( [^A-Z\\s]+\\.)\\s+", "$1\n\n");
return formatted;
}
public static String displayTitle(Bill bill) {
return (bill.short_title != null) ? bill.short_title : bill.official_title;
}
}