package gov.nysenate.openleg.model.agenda;
import gov.nysenate.openleg.model.base.BaseLegislativeContent;
import gov.nysenate.openleg.model.base.SessionYear;
import gov.nysenate.openleg.model.entity.Committee;
import gov.nysenate.openleg.model.entity.CommitteeId;
import java.io.Serializable;
import java.time.LocalDate;
import java.util.*;
import java.util.stream.Collector;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* An Agenda is essentially a list of items (bills) that are brought up for discussion in
* committees. This Agenda class models the agendas closely to how LBDC sends the source data.
* It is comprised of a collection of addenda which either contains committee meeting information
* including bills that are to be brought up, or committee votes.
*/
public class Agenda extends BaseLegislativeContent implements Serializable
{
private static final long serialVersionUID = -6763891242038699549L;
/** The agenda id. */
private AgendaId id;
/** The map of committee info updates keyed by the addendum id. */
private Map<String, AgendaInfoAddendum> agendaInfoAddenda;
/** The map of committee vote updates keyed by the addendum id. */
private Map<String, AgendaVoteAddendum> agendaVoteAddenda;
/** --- Constructors --- */
public Agenda() {
super();
this.setAgendaInfoAddenda(new TreeMap<>());
this.setAgendaVoteAddenda(new TreeMap<>());
}
public Agenda(AgendaId id) {
this();
this.setId(id);
this.setYear(id.getYear());
this.setSession(SessionYear.of(this.getYear()));
}
/** --- Overrides --- */
@Override
public String toString() {
return "Agenda " + id;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null || getClass() != obj.getClass()) return false;
final Agenda other = (Agenda) obj;
return Objects.equals(this.year, other.year) &&
Objects.equals(this.id, other.id) &&
Objects.equals(this.agendaInfoAddenda, other.agendaInfoAddenda) &&
Objects.equals(this.agendaVoteAddenda, other.agendaVoteAddenda) &&
Objects.equals(this.publishedDateTime, other.publishedDateTime);
}
@Override
public int hashCode() {
return Objects.hash(year, id, agendaInfoAddenda, agendaVoteAddenda, publishedDateTime);
}
/** --- Functional Getters/Setters --- */
public AgendaInfoAddendum getAgendaInfoAddendum(String addendumId) {
return this.agendaInfoAddenda.get(addendumId);
}
public void putAgendaInfoAddendum(AgendaInfoAddendum addendum) {
this.agendaInfoAddenda.put(addendum.getId(), addendum);
}
public AgendaVoteAddendum getAgendaVoteAddendum(String addendumId) {
return this.agendaVoteAddenda.get(addendumId);
}
public void putAgendaVoteAddendum(AgendaVoteAddendum addendum) {
this.agendaVoteAddenda.put(addendum.getId(), addendum);
}
/**
* Finds any 'weekOf' set within the info addenda and returns it, If the data is not complete, it could be possible
* that the agenda doesn't have a weekOf set in which case an empty Optional is returned.
* @return Optional<LocalDate>.
*/
public Optional<LocalDate> getWeekOf() {
return agendaInfoAddenda.values().stream()
.filter(ia -> ia.getWeekOf() != null)
.map(ia -> ia.getWeekOf())
.findAny();
}
public Integer totalBillsConsidered() {
return totalBillsConsidered(Optional.empty());
}
public Integer totalBillsConsidered(Optional<CommitteeId> committeeId) {
return agendaInfoAddenda.values().stream()
.flatMap(ia ->
(committeeId.isPresent())
? (ia.getCommitteeInfoMap().containsKey(committeeId.get()))
? Stream.of(ia.getCommitteeInfoMap().get(committeeId.get()))
: Stream.empty()
: ia.getCommitteeInfoMap().values().stream()
)
.map(ic -> ic.getItems().size())
.reduce(0, Integer::sum);
}
public Integer totalBillsVoted() {
return totalBillsVoted(Optional.empty());
}
public Integer totalBillsVoted(Optional<CommitteeId> committeeId) {
return agendaVoteAddenda.values().stream()
.flatMap(ia ->
(committeeId.isPresent())
? (ia.getCommitteeVoteMap().containsKey(committeeId.get()))
? Stream.of(ia.getCommitteeVoteMap().get(committeeId.get()))
: Stream.empty()
: ia.getCommitteeVoteMap().values().stream()
)
.map(ic -> ic.getVotedBills().size())
.reduce(0, Integer::sum);
}
public Long totalCommittees() {
return agendaInfoAddenda.values().stream()
.flatMap(ia -> ia.getCommitteeInfoMap().keySet().stream())
.distinct()
.count();
}
/**
* Returns true if there is data contained in this agenda for the given committee.
*/
public boolean hasCommittee(CommitteeId committeeId) {
return agendaInfoAddenda.values().stream()
.map(a -> a.getCommitteeInfoMap().keySet())
.filter(a -> a.contains(committeeId))
.findAny().isPresent();
}
/**
* Returns a set of committees that have agenda info addenda.
*/
public Set<CommitteeId> getCommittees() {
return agendaInfoAddenda.values().stream().flatMap(a -> a.getCommitteeInfoMap().keySet().stream())
.collect(Collectors.toSet());
}
/** --- Basic Getters/Setters --- */
public AgendaId getId() {
return id;
}
public void setId(AgendaId id) {
this.id = id;
}
public Map<String, AgendaInfoAddendum> getAgendaInfoAddenda() {
return agendaInfoAddenda;
}
public void setAgendaInfoAddenda(Map<String, AgendaInfoAddendum> agendaInfoAddenda) {
this.agendaInfoAddenda = agendaInfoAddenda;
}
public Map<String, AgendaVoteAddendum> getAgendaVoteAddenda() {
return agendaVoteAddenda;
}
public void setAgendaVoteAddenda(Map<String, AgendaVoteAddendum> agendaVoteAddenda) {
this.agendaVoteAddenda = agendaVoteAddenda;
}
/** --- Functional Getters --- */
public List<CommitteeAgendaAddendumId> getCommitteeAgendaAddendumIds(){
List<CommitteeAgendaAddendumId> committeeAgendaAddendumIds = new ArrayList<>();
// committeeAgendaAddendumIds.add(
return agendaInfoAddenda.values().stream()
.flatMap(agendaInfoAddendum -> agendaInfoAddendum.getCommitteeInfoMap().values().stream())
.map(agendaInfoCommittee -> new CommitteeAgendaAddendumId(agendaInfoCommittee.getAgendaId(),
agendaInfoCommittee.getCommitteeId(),
agendaInfoCommittee.getAddendum())).collect(Collectors.toList());
// return committeeAgendaAddendumIds;
}
}