Skip to content

Commit

Permalink
Merge pull request #22 from malice00/JENKINS-61867
Browse files Browse the repository at this point in the history
JENKINS-61867 - Calendar can be configured to show actual builds, polling or both
  • Loading branch information
malice00 authored Jul 30, 2021
2 parents 3954464 + 40ca5fa commit 94d14cb
Show file tree
Hide file tree
Showing 12 changed files with 264 additions and 111 deletions.
17 changes: 16 additions & 1 deletion src/main/java/io/jenkins/plugins/view/calendar/CalendarView.java
Original file line number Diff line number Diff line change
Expand Up @@ -60,10 +60,15 @@
@Restricted(NoExternalUse.class)
public class CalendarView extends ListView {

public static enum CalendarViewEventsType {
ALL, BUILDS, POLLINGS;
}

public static enum CalendarViewType {
MONTH, WEEK, DAY;
}

private CalendarViewEventsType calendarViewEventsType;
private CalendarViewType calendarViewType;

private Boolean useCustomFormats;
Expand Down Expand Up @@ -104,6 +109,14 @@ public CalendarView(final String name) {
super(name);
}

public CalendarViewEventsType getCalendarViewEventsType() {
return defaultIfNull(calendarViewEventsType, CalendarViewEventsType.ALL);
}

public void setCalendarViewEventsType(final CalendarViewEventsType calendarViewEventsType) {
this.calendarViewEventsType = calendarViewEventsType;
}

public CalendarViewType getCalendarViewType() {
return defaultIfNull(calendarViewType, CalendarViewType.WEEK);
}
Expand Down Expand Up @@ -338,6 +351,7 @@ private void validate(final StaplerRequest req) throws Descriptor.FormException
));
final Pattern validDateTimePattern = Pattern.compile("(0[0-9]|1[0-9]|2[0-4]):00:00");

validateEnum(req, "calendarViewEventsType", CalendarViewEventsType.class);
validateEnum(req, "calendarViewType", CalendarViewType.class);
validateRange(req, "weekSettingsFirstDay", 0, 7);

Expand All @@ -351,6 +365,7 @@ private void validate(final StaplerRequest req) throws Descriptor.FormException
}

private void updateFields(final StaplerRequest req) {
setCalendarViewEventsType(CalendarViewEventsType.valueOf(req.getParameter("calendarViewEventsType")));
setCalendarViewType(CalendarViewType.valueOf(req.getParameter("calendarViewType")));

setUseCustomFormats(req.getParameter("useCustomFormats") != null);
Expand Down Expand Up @@ -405,7 +420,7 @@ public List<CalendarEvent> getEvents() throws ParseException {
final Calendar end = RequestUtil.getParamAsCalendar(req, "end");

final Moment now = new Moment();
return new CalendarEventService(now, new CronJobService(now)).getCalendarEvents(getJobs(), range(start, end));
return new CalendarEventService(now, new CronJobService(now)).getCalendarEvents(getJobs(), range(start, end), getCalendarViewEventsType());
}

public String jsonEscape(final String text) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,12 @@
import hudson.Util;
import hudson.model.Job;
import hudson.model.Run;
import io.jenkins.plugins.view.calendar.CalendarView.CalendarViewEventsType;
import io.jenkins.plugins.view.calendar.service.CalendarEventService;
import io.jenkins.plugins.view.calendar.time.Moment;
import io.jenkins.plugins.view.calendar.time.MomentRange;
import io.jenkins.plugins.view.calendar.util.DateUtil;

import org.apache.commons.lang.StringUtils;
import org.kohsuke.accmod.Restricted;
import org.kohsuke.accmod.restrictions.NoExternalUse;
Expand Down Expand Up @@ -233,9 +235,9 @@ public StartedCalendarEvent getNextStartedEvent() {
}

@Override
public ScheduledCalendarEvent getNextScheduledEvent() {
public ScheduledCalendarEvent getNextScheduledEvent(final CalendarViewEventsType eventsType) {
if (nextScheduledEvent == null && build != null) {
nextScheduledEvent = calendarEventService.getNextScheduledEvent(this);
nextScheduledEvent = calendarEventService.getNextScheduledEvent(this, eventsType);
}
return nextScheduledEvent;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@
*/
package io.jenkins.plugins.view.calendar.event;

import io.jenkins.plugins.view.calendar.CalendarView.CalendarViewEventsType;
import hudson.model.Run;

import org.kohsuke.accmod.Restricted;
import org.kohsuke.accmod.restrictions.NoExternalUse;

Expand All @@ -35,5 +37,5 @@ public interface StartedCalendarEvent extends CalendarEvent {

StartedCalendarEvent getNextStartedEvent();

ScheduledCalendarEvent getNextScheduledEvent();
ScheduledCalendarEvent getNextScheduledEvent(final CalendarViewEventsType eventsType);
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
import hudson.model.Run;
import hudson.scheduler.CronTab;
import hudson.util.RunList;
import io.jenkins.plugins.view.calendar.CalendarView.CalendarViewEventsType;
import io.jenkins.plugins.view.calendar.event.CalendarEvent;
import io.jenkins.plugins.view.calendar.event.CalendarEventComparator;
import io.jenkins.plugins.view.calendar.event.CalendarEventFactory;
Expand Down Expand Up @@ -67,28 +68,29 @@ public CalendarEventService(final Moment now, final CronJobService cronJobServic
*
* @param jobs The jobs from which to collect all CalendarEvents
* @param inclusionRange The range for which CalendarEvents should be returned
* @param eventsType The type of event that should be filtered on
* @return List of events that overlap the range
*/
public List<CalendarEvent> getCalendarEvents(final List<? extends Job> jobs, final MomentRange inclusionRange) {
public List<CalendarEvent> getCalendarEvents(final List<? extends Job> jobs, final MomentRange inclusionRange, final CalendarViewEventsType eventsType) {
final List<CalendarEvent> events = new ArrayList<>();

if (now.isBefore(inclusionRange.getStart())) {
events.addAll(getRunningEvents(jobs, inclusionRange));
events.addAll(getRunningEvents(jobs, inclusionRange, eventsType));
if (isValidRange(now.nextMinute(), inclusionRange.getStart().previousMinute())) {
events.addAll(getScheduledEventsBackward(jobs, range(now.nextMinute(), inclusionRange.getStart().previousMinute()), inclusionRange));
events.addAll(getScheduledEventsBackward(jobs, range(now.nextMinute(), inclusionRange.getStart().previousMinute()), inclusionRange, eventsType));
}
events.addAll(getScheduledEventsForward(jobs, inclusionRange, inclusionRange));
events.addAll(getScheduledEventsForward(jobs, inclusionRange, inclusionRange, eventsType));
} else if (now.isSame(inclusionRange.getStart())) {
events.addAll(getRunningEvents(jobs, inclusionRange));
events.addAll(getScheduledEventsForward(jobs, range(now.nextMinute(), inclusionRange.getEnd()), inclusionRange));
events.addAll(getRunningEvents(jobs, inclusionRange, eventsType));
events.addAll(getScheduledEventsForward(jobs, range(now.nextMinute(), inclusionRange.getEnd()), inclusionRange, eventsType));
} else if (now.isSame(inclusionRange.getEnd())) {
events.addAll(getStartedEvents(jobs, inclusionRange, null));
events.addAll(getStartedEvents(jobs, inclusionRange, null, eventsType));
} else if (now.isAfter(inclusionRange.getEnd())) {
events.addAll(getStartedEvents(jobs, inclusionRange, null));
events.addAll(getStartedEvents(jobs, inclusionRange, null, eventsType));
} else { // (now.isAfter(inclusionRange.getStart()) && now.isBefore(inclusionRange.getEnd())
events.addAll(getStartedEvents(jobs, range(inclusionRange.getStart(), now.nextMinute()), null));
events.addAll(getStartedEvents(jobs, range(inclusionRange.getStart(), now.nextMinute()), null, eventsType));
if (isValidRange(now.nextMinute(), inclusionRange.getEnd())) {
events.addAll(getScheduledEventsForward(jobs, range(now.nextMinute(), inclusionRange.getEnd()), inclusionRange));
events.addAll(getScheduledEventsForward(jobs, range(now.nextMinute(), inclusionRange.getEnd()), inclusionRange, eventsType));
}
}

Expand All @@ -97,19 +99,19 @@ public List<CalendarEvent> getCalendarEvents(final List<? extends Job> jobs, fin
return events;
}

public List<ScheduledCalendarEvent> getScheduledEventsForward(final List<? extends Job> jobs, final MomentRange searchRange, final MomentRange inclusionRange) {
return getScheduledEvents(jobs, new ForwardScheduledEventCollector(searchRange, inclusionRange));
public List<ScheduledCalendarEvent> getScheduledEventsForward(final List<? extends Job> jobs, final MomentRange searchRange, final MomentRange inclusionRange, final CalendarViewEventsType eventsType) {
return getScheduledEvents(jobs, new ForwardScheduledEventCollector(searchRange, inclusionRange), eventsType);
}

public List<ScheduledCalendarEvent> getScheduledEventsBackward(final List<? extends Job> jobs, final MomentRange searchRange, final MomentRange inclusionRange) {
return getScheduledEvents(jobs, new BackwardScheduledEventCollector(searchRange, inclusionRange));
public List<ScheduledCalendarEvent> getScheduledEventsBackward(final List<? extends Job> jobs, final MomentRange searchRange, final MomentRange inclusionRange, final CalendarViewEventsType eventsType) {
return getScheduledEvents(jobs, new BackwardScheduledEventCollector(searchRange, inclusionRange), eventsType);
}

public List<ScheduledCalendarEvent> getScheduledEvents(final List<? extends Job> jobs, final ScheduledEventCollector collector) {
public List<ScheduledCalendarEvent> getScheduledEvents(final List<? extends Job> jobs, final ScheduledEventCollector collector, final CalendarViewEventsType eventsType) {
for (final Job job: jobs) {
if (job.isBuildable()) {
final long estimatedDuration = job.getEstimatedDuration();
final List<CronTab> cronTabs = cronJobService.getCronTabs(job);
final List<CronTab> cronTabs = cronJobService.getCronTabs(job, eventsType);
for (final CronTab cronTab: cronTabs) {
collector.collectEvents(job, cronTab, estimatedDuration);
}
Expand Down Expand Up @@ -200,36 +202,38 @@ protected int searchOffset() {
}
}

public List<StartedCalendarEvent> getFinishedEvents(final List<? extends Job> jobs, final MomentRange range) {
return getStartedEvents(jobs, range, CalendarEventState.FINISHED);
public List<StartedCalendarEvent> getFinishedEvents(final List<? extends Job> jobs, final MomentRange range, final CalendarViewEventsType eventsType) {
return getStartedEvents(jobs, range, CalendarEventState.FINISHED, eventsType);
}

public List<StartedCalendarEvent> getRunningEvents(final List<? extends Job> jobs, final MomentRange range) {
return getStartedEvents(jobs, range, CalendarEventState.RUNNING);
public List<StartedCalendarEvent> getRunningEvents(final List<? extends Job> jobs, final MomentRange range, final CalendarViewEventsType eventsType) {
return getStartedEvents(jobs, range, CalendarEventState.RUNNING, eventsType);
}

@SuppressWarnings("PMD.CyclomaticComplexity")
public List<StartedCalendarEvent> getStartedEvents(final List<? extends Job> jobs, final MomentRange range, final CalendarEventState state) {
public List<StartedCalendarEvent> getStartedEvents(final List<? extends Job> jobs, final MomentRange range, final CalendarEventState state, final CalendarViewEventsType eventsType) {
if (state == CalendarEventState.SCHEDULED) {
throw new IllegalArgumentException("State for started events cannot be " + CalendarEventState.SCHEDULED);
}

final List<StartedCalendarEvent> events = new ArrayList<>();
for (final Job job: jobs) {
if (state == CalendarEventState.RUNNING && !job.isBuilding()) {
continue;
}
final RunList<Run> builds = job.getBuilds();
for (final Run build : builds) {
if (state == CalendarEventState.RUNNING && !build.isBuilding()) {
continue;
}
if (state == CalendarEventState.FINISHED && build.isBuilding()) {
if (eventsType != CalendarViewEventsType.POLLINGS) {
for (final Job job: jobs) {
if (state == CalendarEventState.RUNNING && !job.isBuilding()) {
continue;
}
final StartedCalendarEvent event = calendarEventFactory.createStartedEvent(job, build);
if (event.isInRange(range)) {
events.add(event);
final RunList<Run> builds = job.getBuilds();
for (final Run build : builds) {
if (state == CalendarEventState.RUNNING && !build.isBuilding()) {
continue;
}
if (state == CalendarEventState.FINISHED && build.isBuilding()) {
continue;
}
final StartedCalendarEvent event = calendarEventFactory.createStartedEvent(job, build);
if (event.isInRange(range)) {
events.add(event);
}
}
}
}
Expand Down Expand Up @@ -267,9 +271,9 @@ public StartedCalendarEvent getNextEvent(final StartedCalendarEvent event) {
return null;
}

public ScheduledCalendarEvent getNextScheduledEvent(final CalendarEvent event) {
public ScheduledCalendarEvent getNextScheduledEvent(final CalendarEvent event, final CalendarViewEventsType eventsType) {
final Job job = event.getJob();
final Calendar nextStart = cronJobService.getNextStart(job);
final Calendar nextStart = cronJobService.getNextStart(job, eventsType);
if (nextStart != null) {
final long estimatedDuration = job.getEstimatedDuration();
return calendarEventFactory.createScheduledEvent(job, nextStart, estimatedDuration);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@
import hudson.scheduler.CronTabList;
import hudson.scheduler.Hash;
import hudson.triggers.Trigger;
import hudson.triggers.SCMTrigger;
import io.jenkins.plugins.view.calendar.CalendarView.CalendarViewEventsType;
import io.jenkins.plugins.view.calendar.time.Moment;
import io.jenkins.plugins.view.calendar.util.PluginUtil;

Expand Down Expand Up @@ -99,7 +101,8 @@ public List<CronTab> getCronTabs(final Trigger trigger, final Hash hash) {
return cronTabs;
}

public List<Trigger> getCronTriggers(final Job job) {
@SuppressWarnings("PMD.CyclomaticComplexity")
public List<Trigger> getCronTriggers(final Job job, final CalendarViewEventsType eventsType) {
Collection<Trigger<?>> jobTriggers;
if (job instanceof AbstractProject) {
jobTriggers = ((AbstractProject)job).getTriggers().values();
Expand All @@ -111,28 +114,31 @@ public List<Trigger> getCronTriggers(final Job job) {

final List<Trigger> cronTriggers = new ArrayList<>();
for (final Trigger<?> jobTrigger: jobTriggers) {
if (StringUtils.isNotBlank(jobTrigger.getSpec())) {
cronTriggers.add(jobTrigger);
} else if (PluginUtil.hasParameterizedSchedulerPluginInstalled()
&& jobTrigger instanceof ParameterizedTimerTrigger
&& StringUtils.isNotBlank(((ParameterizedTimerTrigger) jobTrigger).getParameterizedSpecification())) {
cronTriggers.add(jobTrigger);
if (eventsType == CalendarViewEventsType.ALL ||
(eventsType == CalendarViewEventsType.BUILDS ^ jobTrigger instanceof SCMTrigger)) {
if (StringUtils.isNotBlank(jobTrigger.getSpec())) {
cronTriggers.add(jobTrigger);
} else if (PluginUtil.hasParameterizedSchedulerPluginInstalled()
&& jobTrigger instanceof ParameterizedTimerTrigger
&& StringUtils.isNotBlank(((ParameterizedTimerTrigger) jobTrigger).getParameterizedSpecification())) {
cronTriggers.add(jobTrigger);
}
}
}
return cronTriggers;
}

public List<CronTab> getCronTabs(final Job job) {
public List<CronTab> getCronTabs(final Job job, final CalendarViewEventsType eventsType) {
final List<CronTab> cronTabs = new ArrayList<CronTab>();
for (final Trigger trigger: getCronTriggers(job)) {
for (final Trigger trigger: getCronTriggers(job, eventsType)) {
cronTabs.addAll(getCronTabs(trigger, Hash.from(job.getFullName())));
}
return cronTabs;
}

public Calendar getNextStart(final Job job) {
public Calendar getNextStart(final Job job, final CalendarViewEventsType eventsType) {
Calendar next = null;
final List<CronTab> cronTabs = getCronTabs(job);
final List<CronTab> cronTabs = getCronTabs(job, eventsType);
for (final CronTab cronTab: cronTabs) {
final Calendar ceil = cronTab.ceil(now.nextMinute().getTimeInMillis());
if (next == null || ceil.before(next)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,12 @@ Portions of this code copied from the Jenkins Project. See LICENSE.md for notice
<link type="text/css" rel="stylesheet" href="${rootURL}/plugin/calendar-view/bundles/calendar-view.css" />

<f:section title="${%Calendar Options}" name="calendarOptions">
<f:entry title="${%Shown events}" field="calendarViewEventsType">
<f:radio name="calendarViewEventsType" value="ALL" checked="${it.calendarViewEventsType=='ALL'}"/>${%All events}
<f:radio name="calendarViewEventsType" value="BUILDS" checked="${it.calendarViewEventsType=='BUILDS'}"/>${%Builds}
<f:radio name="calendarViewEventsType" value="POLLINGS" checked="${it.calendarViewEventsType=='POLLINGS'}"/>${%Planned polling}
</f:entry>

<f:entry title="${%Default View}" field="calendarViewType">
<f:radio name="calendarViewType" value="MONTH" checked="${it.calendarViewType=='MONTH'}"/>${%Month}
<f:radio name="calendarViewType" value="WEEK" checked="${it.calendarViewType=='WEEK'}"/>${%Week}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ Job\ Filters=Job Filter
Add\ Job\ Filter=Job Filter hinzufügen
Status\ Filter=Status-Filter

Shown\ events=Angezeigte Ereignisse
All\ events=Alle Ereignisse
Planned\ polling=Geplante SCM Abfragen

Calendar\ Options=Kalenderoptionen
Default\ View=Standardansicht
Month=Monat
Expand Down
Loading

2 comments on commit 94d14cb

@pespinel
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@malice00 Are you going to create a release with the last changes for this plugin?

@malice00
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@pespinel I have wanted to for a bit, but other things kept coming up. I'll try to get it done soon, currently life looks a little more relaxed.

Please sign in to comment.