/* * Copyright 2009-2012 by KNURT Systeme (http://www.knurt.de) * * Licensed under the Creative Commons License Attribution-NonCommercial-ShareAlike 3.0 Unported; * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://creativecommons.org/licenses/by-nc-sa/3.0/ * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package de.knurt.fam.template.controller.json; import java.io.IOException; import java.util.ArrayList; import java.util.Calendar; import java.util.List; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import de.knurt.fam.core.aspects.logging.FamLog; import de.knurt.fam.core.aspects.security.auth.FamAuth; import de.knurt.fam.core.model.config.BookingRule; import de.knurt.fam.core.model.config.BookingStrategy; import de.knurt.fam.core.model.config.FacilityBookable; import de.knurt.fam.core.model.persist.FacilityAvailability; import de.knurt.fam.core.model.persist.User; import de.knurt.fam.core.model.persist.booking.Booking; import de.knurt.fam.core.persistence.dao.FamDaoProxy; import de.knurt.fam.core.persistence.dao.config.FacilityConfigDao; import de.knurt.fam.core.view.text.FamDateFormat; import de.knurt.heinzelmann.util.nebc.bu.JSONObjectFromRequest; import de.knurt.heinzelmann.util.time.TimeFrame; import de.knurt.heinzelmann.util.time.TimeFrameFactory; /** * return events of the month for a given day that is part of the month and * bookable facility that must be booked time based. * * @author Daniel Oltmanns * @since 1.3.1 (04/07/2011) */ public class GetEventsController extends JSONController { public GetEventsController(User user) { this.user = user; } private User user = null; /** * return an available possible booking. * * there are two different places where this is called: 1. the * "request booking" link in the navigation of the calendar 2. the drag and * drop action in same calender * * the response depends on which place causes the request. * * @param rq * request * @param rs * response * @return an available possible booking. */ @Override public JSONObject getJSONObject(HttpServletRequest rq, HttpServletResponse rs) { JSONObject result = new JSONObject(); boolean succ = true; // get data JSONObject data = null; try { data = new JSONObjectFromRequest().process(rq); if (data == null) { succ = false; FamLog.info("data not found", 201104071047l); } } catch (Exception e) { succ = false; FamLog.info("data not found", 201104071047l); } // get facility from request FacilityBookable facility = null; if (succ) { try { String facilityKey = data.getString("facility"); if (facilityKey != null && FacilityConfigDao.isKey(facilityKey)) { facility = (FacilityBookable) FacilityConfigDao.getInstance().getConfiguredInstance(facilityKey); if (facility.isUnknown()) { succ = false; FamLog.info("requested unknown facility", 201109260828l); } } else { succ = false; } } catch (JSONException e) { succ = false; FamLog.info("facility not found", 201104071011l); } catch (ClassCastException e) { succ = false; FamLog.info("requested unbookable facility", 201109230917l); } } // get capacity_units from request int capacityUnits = -1; if (succ) { try { capacityUnits = Integer.parseInt(data.getString("capacity_units")); } catch (JSONException e) { succ = false; FamLog.info("capacity units not found", 201104130939l); } } // capacity units in bounce? check! BookingRule br = null; if (succ) { br = FacilityConfigDao.bookingRule(facility.getKey()); if (br == null || capacityUnits < br.getMinBookableCapacityUnits(user) || capacityUnits > br.getMaxBookableCapacityUnits(user)) { succ = false; } } // refuse facilities that are not booked time based if (succ && br.getBookingStrategy() != BookingStrategy.TIME_BASED) { succ = false; } // get calendar Calendar dayInMonth = Calendar.getInstance(); // ↘ as a convention we are starting on mondays!!! dayInMonth.setFirstDayOfWeek(2); if (succ && facility != null) { // set year try { int year = data.getInt("year"); dayInMonth.set(Calendar.YEAR, year); } catch (JSONException e) { succ = false; FamLog.info("key year not found", 201104071025l); } // set month try { int month = data.getInt("month"); dayInMonth.set(Calendar.MONTH, month); } catch (JSONException e) { succ = false; FamLog.info("key month not found", 201104071026l); } // set day_of_month try { int day_of_month = data.getInt("day_of_month"); dayInMonth.set(Calendar.DAY_OF_MONTH, day_of_month); } catch (JSONException e) { succ = false; FamLog.info("key month not found", 201104071027l); } } // set success result try { result.put("succ", succ); if (succ) { result.put("events", this.getEventsOfMonthWithFullWeeks(dayInMonth, facility, br, capacityUnits)); } else { result.put("error", "invalid data format"); // INTLANG } } catch (JSONException e) { FamLog.exception(e, 201104071014l); } return result; } /** * return events of a month. * * @param dayInMonth * that is part of that month * @param facility * events are for * @param br * rules user try to book with * @param capacityUnits * requested * @return events of a month */ private JSONArray getEventsOfMonthWithFullWeeks(Calendar dayInMonth, FacilityBookable facility, BookingRule br, int capacityUnits) { JSONArray events = new JSONArray(); TimeFrame requestedMonth = new TimeFrameFactory(dayInMonth).getMonthWithFullWeeks(); List<FacilityAvailability> generalAvailabilities = FamDaoProxy.facilityDao().getFacilityAvailabilitiesMergedByFacilities(requestedMonth, facility.getKey()); List<Booking> bookings = FamDaoProxy.bookingDao().getUncanceledBookingsAndApplicationsIn(facility, requestedMonth); try { // create pointer for loop Calendar monthPointer = (Calendar) requestedMonth.getCalendarStart().clone(); // steps to increment in minutes int oneHour = 60; // for the calendar take 60 minutes for every // facility! // loop while it is the same month (with full weeks) as given while (requestedMonth.contains(monthPointer.getTime())) { // start in "minutes of day" int start = monthPointer.get(Calendar.MINUTE) + monthPointer.get(Calendar.HOUR_OF_DAY) * 60; JSONObject event = new JSONObject(); event.put("start", start); event.put("end", start + oneHour); TimeFrame tf = new TimeFrameFactory(monthPointer).getDuration(Calendar.MINUTE, oneHour); GetEventsControllerEvent pEvent = this.getGetEventsControllerEvent(tf, facility, br, capacityUnits, generalAvailabilities, bookings); event.put("event", pEvent.getNumber()); event.put("label", pEvent.getLabel()); event.put("timeFrameLabel", FamDateFormat.getCustomTimeFrame(pEvent.getTimeFrame(), "E MM/dd/yyyy HH:mm", "HH:mm", " – ")); event.put("month", monthPointer.get(Calendar.MONTH)); event.put("year", monthPointer.get(Calendar.YEAR)); event.put("day_of_month", monthPointer.get(Calendar.DAY_OF_MONTH)); // increment month and destroy summer and winter time int hourBefore = monthPointer.get(Calendar.HOUR_OF_DAY); monthPointer.add(Calendar.MINUTE, oneHour); if (hourBefore == monthPointer.get(Calendar.HOUR_OF_DAY)) { // ↖ clock moved back to winter time // ↘ skip doubled hour monthPointer.add(Calendar.MINUTE, oneHour); } else if (hourBefore == monthPointer.get(Calendar.HOUR_OF_DAY) - 2) { // ↖ clock moved forward to summer time // ↘ enlarge last hour event.put("end", start + oneHour + oneHour); } // now put event events.put(event); } } catch (JSONException e) { FamLog.exception(e, 201104070854l); } return events; } protected GetEventsControllerEvent getGetEventsControllerEvent(TimeFrame timeFrame, FacilityBookable facility, BookingRule br, int capacityUnitsRequested, List<FacilityAvailability> generalAvailabilities, List<Booking> bookings) { GetEventsControllerEvent result = new GetEventsControllerEvent(); boolean somethingFound = false; // check generaly availability for (FacilityAvailability da : generalAvailabilities) { if (da.overlaps(timeFrame) && !da.isCompletelyAvailable()) { String label = ""; if (da.isNotAvailableBecauseOfSuddenFailure()) { label = "Not Available because of a sudden failure!"; // INTLANG } else if (da.isNotAvailableBecauseOfMaintenance()) { label = "Not Available because of a maintenance!"; // INTLANG } else if (da.mustNotStartHere()) { label = "Your booking must not start here."; // INTLANG } else { // opening hours label = "Not Available in general!"; // INTLANG } String notice = da.getNotice(); if (notice != null && !notice.isEmpty()) { label += " Notice: " + notice; } result.setNumber(da.getAvailable()); result.setLabel(label); somethingFound = true; break; } } // time frame is past or user has to wait for booking if (!somethingFound) { Calendar earlistPossibilityToBookFromNow = FamAuth.getEarliestCalendarToBookFromNow(this.user, facility); if (timeFrame.getCalendarStart().before(earlistPossibilityToBookFromNow)) { String eptbfn = FamDateFormat.getDateFormattedWithTime(earlistPossibilityToBookFromNow); result.setLabel(String.format("Please request after: %s", eptbfn)); // INTLANG result.setNumber(FacilityAvailability.GENERAL_NOT_AVAILABLE); somethingFound = true; } } // if still available, check booking situation if (!somethingFound && bookings != null) { // ↖ bookable facility and no maintenance count statuses int capacityUnitsBooked = 0; List<Booking> overlappingBookings = new ArrayList<Booking>(); for (Booking b : bookings) { if (b.overlaps(timeFrame)) { capacityUnitsBooked += b.getCapacityUnits(); overlappingBookings.add(b); } } if (capacityUnitsBooked > 0) { String label = ""; if (((FacilityBookable) facility).getCapacityUnits() >= capacityUnitsBooked + capacityUnitsRequested) { label = String.format("Still %s units available! ", ((FacilityBookable) facility).getCapacityUnits() - capacityUnitsBooked); // INTLANG result.setNumber(FacilityAvailability.MAYBE_AVAILABLE); } else { label = "Booked Out! "; // INTLANG result.setNumber(FacilityAvailability.BOOKED_NOT_AVAILABLE); } if (overlappingBookings.size() == 1) { String userinfo = ""; if (this.user.hasRight(FamAuth.VIEW_PERSONAL_INFORMATION, facility)) { User tmpu = overlappingBookings.get(0).getUser(); userinfo = String.format("%s (%s , %s)", tmpu.getFullName(), tmpu.getMail(), tmpu.getPhone()); } else { userinfo = overlappingBookings.get(0).getUsername(); } label += String.format("Booked by %s.", userinfo); // INTLANG String notice = overlappingBookings.get(0).getNotice(); if (notice != null && !notice.isEmpty()) { label += " Notice: " + notice; // INTLANG } } else { label += String.format("There are %s bookings.", overlappingBookings.size()); // INTLANG } result.setLabel(label); // INTLANG somethingFound = true; } } if (!somethingFound) { result.setLabel("Available!"); // INTLANG result.setNumber(FacilityAvailability.COMPLETE_AVAILABLE); } // add the time the label is for result.setTimeFrame(timeFrame); return result; } /** {@inheritDoc} */ @Override public void onException(IOException ex) { FamLog.exception(ex, 201104070855l); } }