do not attempt get the person object when no one is logged in...
[sgn.git] / lib / CXGN / Calendar.pm
bloba1d22d21ae4a9c6d4e88f3fcf4b69bd8924e5776
2 =head1 NAME
4 CXGN::Calendar - helper class for calendar
6 =head1 SYNOPSYS
8 my $calendar_funcs = CXGN::Calendar->new( { } );
9 $calendar_funcs->check_value_format("2012/01/01 00:00:00");
11 etc.
13 =head1 AUTHOR
16 =head1 METHODS
18 =cut
20 package CXGN::Calendar;
22 use Moose;
23 use Try::Tiny;
24 use SGN::Model::Cvterm;
25 use Time::Piece;
26 use Time::Seconds;
27 use Data::Dumper;
30 has 'bcs_schema' => (
31 isa => 'Bio::Chado::Schema',
32 is => 'rw',
35 has 'sp_person_id' => (
36 isa => "Int",
37 is => 'rw',
40 has 'roles' => (
41 isa => "ArrayRef",
42 is => 'rw',
46 sub check_value_format {
47 my $self = shift;
48 my $value = shift;
50 if ($value) {
51 #Events saved through the calendar will have this format
52 # "YYYY-MM-DDTHH:MM:SS"
53 if ($value =~ /^{"\d{4}-\d\d-\d\dT\d\d:\d\d:\d\d","\d{4}-\d\d-\d\dT\d\d:\d\d:\d\d","/) {
54 return $value;
56 #Dates saved through the trial 'Add Harvest Date' or 'Add Planting Date' will have this format
57 # "YYYY/MM/DD HH:MM:SS"
58 elsif ($value =~ /^\d\d\d\d\/\d\d\/\d\d\s\d\d:\d\d:\d\d$/) {
59 $value = $self->format_time($value)->datetime;
60 return '{"'.$value.'","'.$value.'","","#"}';
62 #Harvest and Planting Dates uploaded via Upload Trial Design files will have this format
63 # "YYYY-MM-DD"
64 elsif ($value =~ /^\d{4}-\d\d-\d\d$/) {
65 $value = $self->format_time($value)->datetime;
66 return '{"'.$value.'","'.$value.'","","#"}';
68 #Historical dates in the database often have this format
69 # "YYYY-Month-DD"
70 elsif ($value =~ /^(\d{4})-(Jan|January|Feb|February|March|Mar|April|Apr|May|June|Jun|July|Jul|August|Aug|September|Sep|October|Oct|November|Nov|December|Dec)-(\d)/) {
71 $value = $self->format_time($value)->datetime;
72 return '{"'.$value.'","'.$value.'","","#"}';
74 else {
75 return;
77 } else {
78 return;
82 sub parse_calendar_array {
83 my $self = shift;
84 my $raw_value = shift;
86 $raw_value =~ tr/{}"//d;
87 my @calendar_array = split(/,/, $raw_value);
88 return @calendar_array;
91 #Displaying 00:00:00 time on mouseover and mouseclick is ugly, so this sub is used to determine date display format, given a datetime string.
92 sub format_display_date {
93 my $self = shift;
94 my $date_display;
95 my $formatted_time = shift;
97 if ($formatted_time->hms('') == '000000') {
98 $date_display = $formatted_time->strftime("%Y-%B-%d");
99 } else {
100 $date_display = $formatted_time->strftime("%Y-%B-%d %H:%M:%S");
102 return $date_display;
105 #FullCalendar's end datetime is exclusive for allday events in the month view. Since all events in the month view are allday = 1, a full day must be added so that it is displayed correctly on the calendar. In the agendaWeek view, not all events are allday = 1, so the end is only modified for allday events.
106 sub calendar_end_display {
107 my $self = shift;
108 my $formatted_time = shift;
109 my $view = shift;
110 my $allday = shift;
112 my $end_time;
113 $end_time = $formatted_time->epoch;
114 if ($view eq 'month' || ($view eq 'agendaWeek' && $allday == 1)) {
115 $end_time += ONE_DAY;
117 $end_time = Time::Piece->strptime($end_time, '%s')->datetime;
118 return $end_time;
121 #On the agendaWeek view, events with start dates with 00:00:00 time are displayed as allDay=true.
122 sub determine_allday {
123 my $self = shift;
124 my $allday;
125 my $formatted_time = shift;
127 if ($formatted_time->hms('') == '000000') {
128 $allday = 1;
129 } else {
130 $allday = 0;
132 return $allday;
135 #This function is used to return a Time::Piece object, which is useful for format consistency. It can take a variety of formats, which is important to match historic date data in the database.
136 sub format_time {
137 my $self = shift;
138 my $input_time = shift;
140 #print STDERR $input_time."\n";
142 my $formatted_time;
144 if ($input_time =~ /^\d{4}-\d\d-\d\d$/) {
145 #print STDERR '1 '.$input_time."\n";
146 $formatted_time = Time::Piece->strptime($input_time, '%Y-%m-%d');
148 if ($input_time =~ /^\d\d\d\d\/\d\d\/\d\d\s\d\d:\d\d:\d\d$/) {
149 #print STDERR '2 '.$input_time."\n";
150 $formatted_time = Time::Piece->strptime($input_time, '%Y/%m/%d %H:%M:%S');
152 if ($input_time =~ /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})$/) {
153 #print STDERR '3 '.$input_time."\n";
154 $formatted_time = Time::Piece->strptime($input_time, '%Y-%m-%dT%H:%M:%S');
156 if ($input_time =~ /^(\d{4})-(Jan|Feb|Mar|Apr|Jun|Jul|Aug|Sep|Oct|Nov|Dec)-(\d{2})$/) {
157 #print STDERR '4 '.$input_time."\n";
158 $formatted_time = Time::Piece->strptime($input_time, '%Y-%b-%d');
160 if ($input_time =~ /^(\d{4})-(Jan|Feb|Mar|Apr|Jun|Jul|Aug|Sep|Oct|Nov|Dec)-(\d{1})$/) {
161 my $single_digit_date = substr($input_time, -1);
162 my $input_time_1 = substr($input_time, 0, -1);
163 $input_time = $input_time_1.'0'.$single_digit_date;
164 #print STDERR '5 '.$input_time."\n";
165 $formatted_time = Time::Piece->strptime($input_time, '%Y-%b-%d');
167 if ($input_time =~ /^(\d{4})-(January|February|March|April|May|June|July|August|September|October|November|December)-(\d{2})$/) {
168 #print STDERR '6 '.$input_time."\n";
169 $formatted_time = Time::Piece->strptime($input_time, '%Y-%B-%d');
171 if ($input_time =~ /^(\d{4})-(January|February|March|April|May|June|July|August|September|October|November|December)-(\d{1})$/) {
172 my $single_digit_date = substr($input_time, -1);
173 my $input_time_1 = substr($input_time, 0, -1);
174 $input_time = $input_time_1.'0'.$single_digit_date;
175 #print STDERR '7 '.$input_time."\n";
176 $formatted_time = Time::Piece->strptime($input_time, '%Y-%B-%d');
178 return $formatted_time;
182 sub get_calendar_events_personal {
183 my $self = shift;
184 my $schema = $self->bcs_schema;
185 my $dbh = $schema->storage->dbh;
186 my $person_id = $self->sp_person_id;
187 my @roles = @{$self->roles};
188 #print STDERR Dumper \@roles;
190 my @search_project_ids = '-1';
191 foreach (@roles) {
192 my $q="SELECT project_id FROM project WHERE name=?";
193 my $sth = $dbh->prepare($q);
194 $sth->execute($_);
195 while (my ($project_id) = $sth->fetchrow_array ) {
196 push(@search_project_ids, $project_id);
198 my $q="SELECT subject_project_id FROM project_relationship JOIN cvterm ON (type_id=cvterm_id) WHERE object_project_id=? and cvterm.name='breeding_program_trial_relationship'";
199 my $sth = $dbh->prepare($q);
200 $sth->execute($project_id);
201 while (my ($trial) = $sth->fetchrow_array ) {
202 push(@search_project_ids, $trial);
207 @search_project_ids = map{$_='me.project_id='.$_; $_} @search_project_ids;
208 my $search_projects = join(" OR ", @search_project_ids);
209 my $search_rs = $schema->resultset('Project::Project')->search(
210 undef,
211 {join=>{'projectprops'=>{'type'=>'cv'}},
212 '+select'=> ['projectprops.projectprop_id', 'type.name', 'projectprops.value', 'type.cvterm_id'],
213 '+as'=> ['pp_id', 'cv_name', 'pp_value', 'cvterm_id'],
216 $search_rs = $search_rs->search([$search_projects]);
217 return $search_rs;
220 sub populate_calendar_events {
221 my $self = shift;
222 my $search_rs = shift;
223 my $view = shift;
224 my @events;
225 my $allday;
226 my $start_time;
227 my $start_drag;
228 my $start_display;
229 my $end_time;
230 my $end_drag;
231 my $end_display;
232 my $formatted_time;
233 my @calendar_array;
234 my $title;
235 my $property;
236 while (my $result = $search_rs->next) {
238 #Check if project property value is an event, and if it is not, then skip to next result.
239 my $calendar_formatted_value = $self->check_value_format($result->get_column('pp_value'));
240 if (!$calendar_formatted_value) {
241 next;
244 #print STDERR $calendar_formatted_value;
246 @calendar_array = $self->parse_calendar_array($calendar_formatted_value);
247 if (!$calendar_array[0]) {
248 next;
251 #We begin with the start datetime, or the first element in the @calendar_array.
252 #A time::piece object is returned from format_time(). Calling ->datetime on this object returns an ISO8601 datetime string. This string is what is used as the calendar event's start. Using format_display_date(), a nice date to display on mouse over is returned.
253 $formatted_time = $self->format_time($calendar_array[0]);
254 $start_time = $formatted_time->datetime;
255 $start_display = $self->format_display_date($formatted_time);
257 #Because fullcalendar does not allow event resizing of allDay=false events in the month view, the allDay parameter must be set depending on the view. The allDay parameter for the agendaWeek view is important and is set using determine_allday().
258 if ($view eq 'month') {
259 $allday = 1;
260 } elsif ($view eq 'agendaWeek') {
261 $allday = $self->determine_allday($formatted_time);
264 #Then we process the end datetime, which is the second element in the calendar_array. calendar_end_display determines what the calendar should display as the end, and format_display_date() returns a nice date to display on mouseover.
265 $formatted_time = $self->format_time($calendar_array[1]);
266 $end_time = $self->calendar_end_display($formatted_time, $view, $allday);
267 $end_display = $self->format_display_date($formatted_time);
269 #Because FullCallendar's end date is exclusive, an end datetime with 00:00:00 will be displayed as one day short on the calendar, and so corrections to the event's end must be made. To facilitate event dragging, an event.end_drag property is used.
270 $end_drag = $formatted_time->datetime;
272 #To display the project name and project properties nicely in the mouseover and more info, we capitalize the first letter of each word.
273 $title = $result->name;
274 #$title =~ s/([\w']+)/\u\L$1/g;
275 $property = $result->get_column('cv_name');
276 $property =~ s/([\w']+)/\u\L$1/g;
278 #Variables are pushed into the event array and will become properties of Fullcalendar events, like event.start, event.cvterm_url, etc.
279 push(@events, {projectprop_id=>$result->get_column('pp_id'), title=>$title, property=>$property, start=>$start_time, start_drag=>$start_time, start_display=>$start_display, end=>$end_time, end_drag=>$end_drag, end_display=>$end_display, project_id=>$result->project_id, project_url=>'/breeders_toolbox/trial/'.$result->project_id.'/', cvterm_id=>$result->get_column('cvterm_id'), cvterm_url=>'/cvterm/'.$result->get_column('cvterm_id').'/view', allDay=>$allday, p_description=>$result->description, event_description=>$calendar_array[2], event_url=>$calendar_array[3]});
281 return \@events;
284 #Takes an event string, which is the value stored in the database for events, and return a nice start date.
285 sub display_start_date {
286 my $self = shift;
287 my $value = shift;
289 my $checked_value = $self->check_value_format($value);
290 if ($checked_value) {
291 my @calendar_array = $self->parse_calendar_array($checked_value);
292 if ($calendar_array[0]) {
293 my $formatted_time = $self->format_time($calendar_array[0]);
294 my $start_display = $self->format_display_date($formatted_time);
295 return $start_display;
296 } else {
297 return;
299 } else {
300 return;
304 #Takes an event string, which is the value stored in the database for events, and return a nice end date.
305 sub display_end_date {
306 my $self = shift;
307 my $value = shift;
309 my $checked_value = $self->check_value_format($value);
310 if ($checked_value) {
311 my @calendar_array = $self->parse_calendar_array($checked_value);
312 if ($calendar_array[1]) {
313 my $formatted_time = $self->format_time($calendar_array[1]);
314 my $end_display = $self->format_display_date($formatted_time);
315 return $end_display;
316 } else {
317 return;
319 } else {
320 return;
324 #Takes an event string, which is the value stored in the database for events, and returns the description.
325 sub display_description {
326 my $self = shift;
327 my $value = shift;
329 my $checked_value = $self->check_value_format($value);
330 if ($checked_value) {
331 my @calendar_array = $self->parse_calendar_array($checked_value);
332 if ($calendar_array[2]) {
333 my $description = $calendar_array[2];
334 return $description;
335 } else {
336 return;
338 } else {
339 return;
343 #Takes an event string, which is the value stored in the database for events, and returns the url.
344 sub display_url {
345 my $self = shift;
346 my $value = shift;
348 my $checked_value = $self->check_value_format($value);
349 if ($checked_value) {
350 my @calendar_array = $self->parse_calendar_array($checked_value);
351 if ($calendar_array[3]) {
352 my $url = $calendar_array[3];
353 return $url;
354 } else {
355 return;
357 } else {
358 return;