package scrum.client.project;
import ilarkesto.core.base.Utl;
import ilarkesto.core.logging.Log;
import ilarkesto.core.scope.Scope;
import ilarkesto.gwt.client.AGwtEntity;
import ilarkesto.gwt.client.Date;
import ilarkesto.gwt.client.DateAndTime;
import ilarkesto.gwt.client.Gwt;
import ilarkesto.gwt.client.HyperlinkWidget;
import ilarkesto.gwt.client.Time;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import scrum.client.admin.Auth;
import scrum.client.admin.ProjectUserConfig;
import scrum.client.admin.User;
import scrum.client.calendar.SimpleEvent;
import scrum.client.collaboration.Comment;
import scrum.client.collaboration.ForumSupport;
import scrum.client.collaboration.Subject;
import scrum.client.collaboration.UsersStatus;
import scrum.client.collaboration.Wikipage;
import scrum.client.common.ShowEntityAction;
import scrum.client.files.File;
import scrum.client.impediments.Impediment;
import scrum.client.issues.Issue;
import scrum.client.journal.ProjectEvent;
import scrum.client.pr.BlogEntry;
import scrum.client.release.Release;
import scrum.client.risks.Risk;
import scrum.client.sprint.Sprint;
import scrum.client.sprint.Task;
import com.google.gwt.user.client.ui.Widget;
public class Project extends GProject implements ForumSupport {
private static transient final Log LOG = Log.get(Project.class);
private static final String effortUnit = "pts";
public static final String INIT_LABEL = "New Project";
private transient Comparator<Requirement> requirementsOrderComparator;
private transient Comparator<Issue> issuesOrderComparator;
public Project(User creator) {
setLabel(INIT_LABEL + " " + DateAndTime.now());
addParticipant(creator);
addAdmin(creator);
addProductOwner(creator);
addScrumMaster(creator);
addTeamMember(creator);
setLastOpenedDateAndTime(DateAndTime.now());
}
public Project(Map data) {
super(data);
}
@Override
public boolean isEditable() {
User currentUser = Scope.get().getComponent(Auth.class).getUser();
return currentUser.isAdmin() || isAdmin(currentUser);
}
public List<Issue> getClaimedBugs(User user) {
List<Issue> issues = new ArrayList<Issue>();
for (Issue issue : getBugs()) {
if (issue.isOwner(user) && !issue.isFixed()) issues.add(issue);
}
return issues;
}
public List<Task> getClaimedTasks(User user) {
List<Task> tasks = new ArrayList<Task>();
for (Requirement req : getRequirements()) {
tasks.addAll(req.getClaimedTasks(user));
}
return tasks;
}
public String getVelocitiesFromLastSprints() {
StringBuilder sb = new StringBuilder();
List<Sprint> sprints = getCompletedSprints();
Collections.sort(sprints, Sprint.END_DATE_COMPARATOR);
float sum = 0;
int count = 0;
for (Sprint sprint : sprints) {
Float velocity = sprint.getVelocity();
if (velocity == null) continue;
String velocityString = velocity < 1 ? velocity.toString() : String.valueOf(velocity.intValue());
sb.insert(0, velocityString + ", ");
sum += velocity;
count++;
if (count >= 12) break;
}
if (count > 0 && sum > 0) {
float avarage = sum / count;
String avarageString = String.valueOf(avarage);
int idx = avarageString.lastIndexOf('.');
if (idx > 0 && avarageString.length() > idx + 1) {
avarageString = avarageString.substring(0, idx + 2);
}
sb.append("[ ").append(avarageString).append(" avg. ]");
}
return sb.toString();
}
public Float getVelocityFromLastSprint() {
Sprint latest = getLatestCompletedSprint();
return latest == null ? null : latest.getVelocity();
}
public Sprint getLatestCompletedSprint() {
Sprint latest = null;
for (Sprint sprint : getSprints()) {
if (!sprint.isCompleted()) continue;
if (latest == null || sprint.getEnd().isAfter(latest.getEnd())) latest = sprint;
}
return latest;
}
public List<Sprint> getCompletedSprints() {
List<Sprint> ret = new ArrayList<Sprint>();
for (Sprint sprint : getSprints()) {
if (sprint.isCompleted()) ret.add(sprint);
}
return ret;
}
public List<SimpleEvent> getSimpleEvents() {
return getDao().getSimpleEventsByProject(this);
}
public List<ProjectEvent> getProjectEvents() {
return getDao().getProjectEventsByProject(this);
}
public List<ProjectEvent> getLatestProjectEvents(int min) {
List<ProjectEvent> events = getProjectEvents();
Collections.sort(events, ProjectEvent.DATE_AND_TIME_COMPARATOR);
DateAndTime deadline = new DateAndTime(Date.today().prevDay(), Time.now());
List<ProjectEvent> ret = new ArrayList<ProjectEvent>();
int count = 0;
for (ProjectEvent event : events) {
ret.add(event);
count++;
DateAndTime dateAndTime = event.getDateAndTime();
if (count > min && dateAndTime.isBefore(deadline)) break;
}
return ret;
}
public List<Wikipage> getWikipages() {
return getDao().getWikipagesByProject(this);
}
public List<File> getFiles() {
return getDao().getFilesByProject(this);
}
public Wikipage getWikipage(String name) {
name = name.toLowerCase();
for (Wikipage page : getDao().getWikipagesByProject(this)) {
if (page.getName().toLowerCase().equals(name)) return page;
}
return null;
}
public ProjectUserConfig getUserConfig(User user) {
for (ProjectUserConfig config : getDao().getProjectUserConfigsByProject(this)) {
if (config.isUser(user)) { return config; }
}
throw new IllegalStateException("User has no project config: " + user);
}
// TODO: guten namen für die methode finden.. hieß vorher isPig(), allerdings ist der PO eig. kein Pig,
// oder?
public boolean isScrumTeamMember(User user) {
return isProductOwner(user) || isScrumMaster(user) || isTeamMember(user);
}
public boolean isProductOwnerOrScrumMaster(User user) {
return isProductOwner(user) || isScrumMaster(user);
}
public boolean isProductOwnerOrTeamMember(User user) {
return isProductOwner(user) || isTeamMember(user);
}
public boolean isParticipant(User user) {
return getParticipants().contains(user);
}
public boolean isTeamMember(User user) {
return getTeamMembers().contains(user);
}
public boolean isScrumMaster(User user) {
return getScrumMasters().contains(user);
}
public boolean isProductOwner(User user) {
return getProductOwners().contains(user);
}
public boolean isAdmin(User user) {
return getAdmins().contains(user);
}
public String getUsersRolesAsString(User user, String prefix, String suffix) {
StringBuilder sb = new StringBuilder();
List<String> roles = new ArrayList<String>();
if (isProductOwner(user)) roles.add("PO");
if (isScrumMaster(user)) roles.add("SM");
if (isTeamMember(user)) roles.add("T");
boolean first = true;
if (!roles.isEmpty()) {
for (String role : roles) {
if (first) {
first = false;
if (prefix != null) sb.append(prefix);
} else {
sb.append(",");
}
sb.append(role);
}
if (suffix != null) sb.append(suffix);
}
return sb.toString();
}
private void updateRequirementsOrder() {
List<Requirement> requirements = getRequirements();
Collections.sort(requirements, getRequirementsOrderComparator());
updateRequirementsOrder(requirements);
}
public void updateRequirementsOrder(List<Requirement> requirements) {
setRequirementsOrderIds(Gwt.getIdsAsList(requirements));
}
public void updateUrgentIssuesOrder(List<Issue> issues) {
setUrgentIssuesOrderIds(Gwt.getIdsAsList(issues));
}
public void setParticipantsConfigured(Collection<User> users) {
users.addAll(getTeamMembers());
users.addAll(getAdmins());
users.addAll(getProductOwners());
users.addAll(getScrumMasters());
setParticipants(users);
}
public String getEffortUnit() {
return effortUnit;
}
public Wikipage createNewWikipage(String name) {
Wikipage page = new Wikipage(this, name);
getDao().createWikipage(page);
return page;
}
public Subject createNewSubject() {
Subject subject = new Subject(this);
getDao().createSubject(subject);
return subject;
}
public Issue createNewIssue() {
Issue issue = new Issue(this);
getDao().createIssue(issue);
return issue;
}
public Impediment createNewImpediment() {
Impediment impediment = new Impediment(this);
getDao().createImpediment(impediment);
return impediment;
}
public void deleteImpediment(Impediment impediment) {
getDao().deleteImpediment(impediment);
}
public void deleteFile(File file) {
getDao().deleteFile(file);
}
public void deleteIssue(Issue issue) {
getDao().deleteIssue(issue);
}
public void deleteRisk(Risk risk) {
getDao().deleteRisk(risk);
}
public Set<ForumSupport> getEntitiesWithComments() {
Set<ForumSupport> ret = new HashSet<ForumSupport>();
ret.addAll(getSubjects());
for (Comment comment : getDao().getComments()) {
AGwtEntity entity = comment.getParent();
if (!(entity instanceof ForumSupport)) {
LOG.error(entity.getClass().getName() + " needs to implement ForumSupport");
continue;
}
ret.add((ForumSupport) comment.getParent());
}
return ret;
}
public List<Impediment> getOpenImpediments() {
List<Impediment> ret = new ArrayList<Impediment>();
for (Impediment impediment : getImpediments()) {
if (impediment.isClosed()) continue;
ret.add(impediment);
}
return ret;
}
public List<Impediment> getImpediments() {
return getDao().getImpedimentsByProject(this);
}
public List<Subject> getSubjects() {
return getDao().getSubjectsByProject(this);
}
public List<Issue> getOpenIssues(boolean includeSuspended) {
List<Issue> ret = new ArrayList<Issue>();
for (Issue issue : getIssues()) {
if (!issue.isOpen()) continue;
if (!includeSuspended && issue.isSuspended()) continue;
ret.add(issue);
}
return ret;
}
public List<Issue> getIdeas() {
List<Issue> ret = new ArrayList<Issue>();
for (Issue issue : getIssues()) {
if (issue.isIdea()) ret.add(issue);
}
return ret;
}
public List<Issue> getUnclaimedBugs(int severity) {
List<Issue> ret = new ArrayList<Issue>();
for (Issue issue : getBugs()) {
if (issue.getOwner() == null && issue.isSeverity(severity)) ret.add(issue);
}
return ret;
}
public List<Issue> getBugs() {
List<Issue> ret = new ArrayList<Issue>();
for (Issue issue : getIssues()) {
if (issue.isBug()) ret.add(issue);
}
return ret;
}
public List<Issue> getFixedBugs() {
List<Issue> ret = new ArrayList<Issue>();
for (Issue issue : getIssues()) {
if (issue.isBug() && issue.isFixed()) ret.add(issue);
}
return ret;
}
public List<Issue> getClosedIssues() {
List<Issue> ret = new ArrayList<Issue>();
for (Issue issue : getIssues()) {
if (issue.isClosed()) ret.add(issue);
}
return ret;
}
public List<Issue> getIssues() {
return getDao().getIssuesByProject(this);
}
public List<Risk> getRisks() {
return getDao().getRisksByProject(this);
}
public List<Release> getReleases() {
return getDao().getReleasesByProject(this);
}
public List<Release> getReleasedReleases() {
List<Release> ret = new ArrayList<Release>();
for (Release release : getReleases()) {
if (release.isReleased()) ret.add(release);
}
return ret;
}
public List<Release> getPlannedReleases() {
List<Release> ret = new ArrayList<Release>();
for (Release release : getReleases()) {
if (!release.isReleased()) ret.add(release);
}
return ret;
}
public List<Risk> getHighestRisks(int max) {
List<Risk> ret = getRisks();
Collections.sort(ret, Risk.PRIORITY_COMPARATOR);
while (ret.size() > max) {
ret.remove(ret.size() - 1);
}
return ret;
}
public Risk createNewRisk() {
Risk risk = new Risk(this);
getDao().createRisk(risk);
return risk;
}
public BlogEntry createNewBlogEntry() {
BlogEntry blogEntry = new BlogEntry(this);
getDao().createBlogEntry(blogEntry);
return blogEntry;
}
public BlogEntry createNewBlogEntry(Release release) {
BlogEntry blogEntry = new BlogEntry(this);
blogEntry.setTitle("Release " + release.getLabel());
blogEntry.setText(release.getIzemizedReleaseNotes());
getDao().createBlogEntry(blogEntry);
return blogEntry;
}
public Release createNewRelease(Release parentRelease) {
Release release = new Release(this);
Date date = Date.today();
if (parentRelease != null) {
release.setParentRelease(parentRelease);
release.setLabel(parentRelease.getLabel() + " Bugfix " + (parentRelease.getBugfixReleases().size() + 1));
Date parentDate = parentRelease.getReleaseDate();
if (parentDate != null && parentDate.isAfter(date)) date = parentDate;
}
release.setReleaseDate(date);
getDao().createRelease(release);
return release;
}
public Requirement createNewRequirement() {
Requirement item = new Requirement(this);
getDao().createRequirement(item);
updateRequirementsOrder();
return item;
}
public Quality createNewQuality() {
Quality item = new Quality(this);
getDao().createQuality(item);
return item;
}
public void deleteRequirement(Requirement item) {
getDao().deleteRequirement(item);
}
public void deleteQuality(Quality item) {
getDao().deleteQuality(item);
}
public List<Requirement> getProductBacklogRequirements() {
List<Requirement> ret = new ArrayList<Requirement>();
for (Requirement requirement : getRequirements()) {
if (requirement.isClosed() && !requirement.isInCurrentSprint()) continue;
ret.add(requirement);
}
return ret;
}
public List<Requirement> getRequirements() {
return getDao().getRequirementsByProject(this);
}
public List<Task> getTasks() {
return getDao().getTasks();
}
public List<Requirement> getRequirementsOrdered() {
List<Requirement> requirements = getRequirements();
Collections.sort(requirements, getRequirementsOrderComparator());
return requirements;
}
public List<Quality> getQualitys() {
return getDao().getQualitysByProject(this);
}
public List<BlogEntry> getBlogEntrys() {
return getDao().getBlogEntrysByProject(this);
}
public Sprint createNewSprint() {
Sprint sprint = new Sprint(this, "New Sprint");
getDao().createSprint(sprint);
return sprint;
}
public List<Sprint> getSprints() {
return getDao().getSprintsByProject(this);
}
public boolean deleteTask(Task task) {
for (Requirement requirement : getRequirements()) {
boolean b = requirement.getTasks().remove(task);
if (b) return true;
}
return false;
}
@Override
public String toString() {
return getLabel();
}
public Comparator<Issue> getIssuesOrderComparator() {
if (issuesOrderComparator == null) issuesOrderComparator = new Comparator<Issue>() {
@Override
public int compare(Issue a, Issue b) {
List<String> order = getUrgentIssuesOrderIds();
int additional = order.size();
int ia = order.indexOf(a.getId());
if (ia < 0) {
ia = additional;
additional++;
}
int ib = order.indexOf(b.getId());
if (ib < 0) {
ib = additional;
additional++;
}
return ia - ib;
}
};
return issuesOrderComparator;
}
public Comparator<Requirement> getRequirementsOrderComparator() {
if (requirementsOrderComparator == null) requirementsOrderComparator = new Comparator<Requirement>() {
@Override
public int compare(Requirement a, Requirement b) {
if (a.isInCurrentSprint() && !b.isInCurrentSprint()) return -1;
if (b.isInCurrentSprint() && !a.isInCurrentSprint()) return 1;
List<String> order = getRequirementsOrderIds();
int additional = order.size();
int ia = order.indexOf(a.getId());
if (ia < 0) {
ia = additional;
additional++;
}
int ib = order.indexOf(b.getId());
if (ib < 0) {
ib = additional;
additional++;
}
return ia - ib;
}
};
return requirementsOrderComparator;
}
public String formatEfford(Float i) {
if (i == null || i == 0) return "nothing";
String unit = getEffortUnit();
if (i == 1) {
if (unit.endsWith("s")) unit = unit.substring(0, unit.length() - 2);
return "1 " + unit;
}
return i + " " + unit;
}
public static final Comparator<Project> LABEL_COMPARATOR = new Comparator<Project>() {
@Override
public int compare(Project a, Project b) {
return a.getLabel().compareTo(b.getLabel());
}
};
public static final Comparator<Project> LAST_OPENED_COMPARATOR = new Comparator<Project>() {
@Override
public int compare(Project a, Project b) {
return Utl.compare(a.getLastOpenedDateAndTime(), b.getLastOpenedDateAndTime()) * -1;
}
};
public Set<User> getUsersSelecting(AGwtEntity entity) {
UsersStatus usersStatus = Scope.get().getComponent(UsersStatus.class);
Set<User> users = new HashSet<User>();
for (User user : getParticipants()) {
if (usersStatus.getSelectedEntitysIds(user).contains(entity.getId())) {
users.add(user);
}
}
return users;
}
@Override
public Widget createForumItemWidget() {
return new HyperlinkWidget(new ShowEntityAction(this, "Project Dashboard"));
}
@Override
public String getReference() {
return "prj";
}
}