diff --git a/Changelog b/Changelog new file mode 100644 index 000000000..4f56b464e --- /dev/null +++ b/Changelog @@ -0,0 +1,36 @@ +1.7.0 - April 4, 2011 + * Recording and automatic scheduling/broadcasting of live shows + o Recording/rebroadcast status of a show is shown in "Now Playing" and "Calendar" + o Can rebroadcast a show at multiple times and dates + * Automatic upload of recorded shows to Soundcloud + * Frontend JQuery widgets for public-facing websites to show your visitors what's playing and the upcoming schedule. + * Ability to over-book a show and automatically cut and fade out song if it goes beyond the show time + * Ability to delete audio files + * Ability to cancel the currently playing show + * Any changes to the schedule are immediately seen by the playout engine + o In version 1.6, you had to make sure that your show was ready to go 30 seconds before it started. + * Upgrade support (should be able to upgrade from any version, unlike 1.6.1 which required an uninstall of 1.6.0 first) + * "Now Playing" list view: + o audio items are now grouped by show. + o If a show is not fully scheduled, the user is notified how many seconds of silence are at the end of the show in this View. + o Audio items that play past the show's end time have a visual notification that they will be cut off + * Ability to change metadata tag display format for web streams + * Config files moved to /etc/airtime. This means all config files are in one convenient location and separated from the code, so you can upgrade your code independently of your config files. + * Redesign of Preferences screen + * Bug fixes: + o CC-2082 OGG stream dies after every song when using MPlayer + o CC-1894 Warn users about time zone differences or clock drift problems on the server + o CC-2058 Utilities are not in the system $PATH + o CC-2051 Unable to change user password + o CC-2030 Icon needed for Cue In/Out + o CC-1955 Special character support in the library search + +1.6.1 - Feb 23, 2011 +Bug fixes: + * CC-1973 Liquidsoap crashes after multi-day playout + * CC-1970 API key fix (Security fix) - Each time you run the install scripts, a new API key is now generated. + * CC-1992 Editing metadata goes blank on 'submit' + * CC-1993 ui start time and song time unsynchronized + +1.6.0 - Feb 14, 2011 +First official version of Airtime. diff --git a/application/models/Schedule.php b/application/models/Schedule.php index 07a23b81c..0fb1468e0 100644 --- a/application/models/Schedule.php +++ b/application/models/Schedule.php @@ -117,6 +117,7 @@ class ScheduleGroup { $itemStartTime = $CC_DBC->getOne("SELECT TIMESTAMP '$itemStartTime' + INTERVAL '$trackLength'"); } } + RabbitMq::PushSchedule(); return $this->groupId; } @@ -244,76 +245,6 @@ class Schedule { return ($count == '0'); } - public static function getTimeUnScheduledInRange($instance_id, $s_datetime, $e_datetime) { - global $CC_CONFIG, $CC_DBC; - - $sql = "SELECT SUM(clip_length) FROM $CC_CONFIG[scheduleTable]" - ." WHERE instance_id = $instance_id"; - - $time = $CC_DBC->GetOne($sql); - - if(is_null($time)) - $time = 0; - - $sql = "SELECT TIMESTAMP '{$e_datetime}' - TIMESTAMP '{$s_datetime}'"; - $length = $CC_DBC->GetOne($sql); - - $sql = "SELECT INTERVAL '{$length}' - INTERVAL '{$time}'"; - $time_left =$CC_DBC->GetOne($sql); - - return $time_left; - } - - public static function getTimeScheduledInRange($s_datetime, $e_datetime) { - global $CC_CONFIG, $CC_DBC; - - $sql = "SELECT SUM(clip_length) FROM ".$CC_CONFIG["scheduleTable"]." - WHERE (starts >= '{$s_datetime}') - AND (ends <= '{$e_datetime}')"; - - $res = $CC_DBC->GetOne($sql); - - if(is_null($res)) - return 0; - - return $res; - } - - public static function GetTotalShowTime($instance_id) { - global $CC_CONFIG, $CC_DBC; - - $sql = "SELECT SUM(clip_length) FROM $CC_CONFIG[scheduleTable]" - ." WHERE instance_id = $instance_id"; - - $res = $CC_DBC->GetOne($sql); - - if(is_null($res)) - return 0; - - return $res; - } - - public static function GetPercentScheduled($instance_id, $s_datetime, $e_datetime) - { - $time = Schedule::GetTotalShowTime($instance_id); - - $s_epoch = strtotime($s_datetime); - $e_epoch = strtotime($e_datetime); - - $con = Propel::getConnection(CcSchedulePeer::DATABASE_NAME); - $sql = "SELECT EXTRACT(EPOCH FROM INTERVAL '{$time}')"; - $r = $con->query($sql); - $i_epoch = $r->fetchColumn(0); - - $percent = ceil(($i_epoch / ($e_epoch - $s_epoch)) * 100); - - if ($percent > 100) - $percent = 100; - - return $percent; - } - - /** * Return TRUE if file is going to be played in the future. * @@ -603,7 +534,7 @@ class Schedule { * @param string $p_time * @return int */ - private static function WallTimeToMillisecs($p_time) + public static function WallTimeToMillisecs($p_time) { $t = explode(":", $p_time); $millisecs = 0; diff --git a/application/models/Shows.php b/application/models/Shows.php index 32cb52fba..0819f762a 100644 --- a/application/models/Shows.php +++ b/application/models/Shows.php @@ -929,24 +929,37 @@ class Show { public static function getFullCalendarEvents($start, $end, $editable=false) { $events = array(); + $options = array(); + + $start_range = new DateTime($start); + $end_range = new DateTime($end); + $interval = $start_range->diff($end_range); + $days = $interval->format('%a'); $shows = Show::getShows($start, $end); $today_timestamp = date("Y-m-d H:i:s"); foreach ($shows as $show) { - if ($editable && strtotime($today_timestamp) < strtotime($show["starts"])) - $events[] = Show::makeFullCalendarEvent($show, array("editable" => true)); - else - $events[] = Show::makeFullCalendarEvent($show); + //only bother calculating percent for week or day view. + if(intval($days) <= 7) { + $show_instance = new ShowInstance($show["instance_id"]); + $options["percent"] = $show_instance->getPercentScheduled(); + } + + if ($editable && strtotime($today_timestamp) < strtotime($show["starts"])) { + $options["editable"] = true; + $events[] = Show::makeFullCalendarEvent($show, $options); + } + else { + $events[] = Show::makeFullCalendarEvent($show, $options); + } } return $events; } - private static function makeFullCalendarEvent($show, $options=array()) + private static function makeFullCalendarEvent($show, $options=array()) { - global $CC_DBC; - $event = array(); if($show["rebroadcast"]) { @@ -976,9 +989,6 @@ class Show { $event[$key] = $value; } - $percent = Schedule::GetPercentScheduled($show["instance_id"], $show["starts"], $show["ends"]); - $event["percent"] = $percent; - return $event; } @@ -1138,6 +1148,13 @@ class ShowInstance { RabbitMq::PushSchedule(); } + public function updateScheduledTime() + { + $con = Propel::getConnection(CcShowInstancesPeer::DATABASE_NAME); + $showInstance = CcShowInstancesQuery::create()->findPK($this->_instanceId); + $showInstance->updateDbTimeFilled($con); + } + public function moveScheduledShowContent($deltaDay, $deltaHours, $deltaMin) { global $CC_DBC; @@ -1268,6 +1285,7 @@ class ShowInstance { $groupId = $sched->addPlaylistAfter($this->_instanceId, $lastGroupId, $plId); } RabbitMq::PushSchedule(); + $this->updateScheduledTime(); } /** @@ -1287,6 +1305,7 @@ class ShowInstance { $groupId = $sched->addFileAfter($this->_instanceId, $lastGroupId, $file_id); } RabbitMq::PushSchedule(); + $this->updateScheduledTime(); } /** @@ -1322,6 +1341,7 @@ class ShowInstance { $CC_DBC->query($sql); RabbitMq::PushSchedule(); + $this->updateScheduledTime(); } public function clearShow() @@ -1330,6 +1350,7 @@ class ShowInstance { ->filterByDbInstanceId($this->_instanceId) ->delete(); RabbitMq::PushSchedule(); + $this->updateScheduledTime(); } public function deleteShow() @@ -1347,7 +1368,7 @@ class ShowInstance { $showInstance->setDbRecordedFile($file_id) ->save(); - $rebroadcasts = CcShowInstancesQuery::create() + $rebroadcasts = CcShowInstancesQuery::create() ->filterByDbOriginalShow($this->_instanceId) ->find(); @@ -1355,32 +1376,36 @@ class ShowInstance { $rebroad = new ShowInstance($rebroadcast->getDbId()); $rebroad->addFileToShow($file_id); - RabbitMq::PushSchedule(); } } public function getTimeScheduled() { - $instance_id = $this->getShowInstanceId(); - $time = Schedule::GetTotalShowTime($instance_id); - return $time; - } + $showInstance = CcShowInstancesQuery::create()->findPK($this->_instanceId); + $time = $showInstance->getDbTimeFilled(); - public function getTimeUnScheduled() - { - $start_timestamp = $this->getShowStart(); - $end_timestamp = $this->getShowEnd(); - $instance_id = $this->getShowInstanceId(); - $time = Schedule::getTimeUnScheduledInRange($instance_id, $start_timestamp, $end_timestamp); - return $time; + if(is_null($time)) { + $time = "00:00:00"; + } + return $time; } public function getPercentScheduled() { $start_timestamp = $this->getShowStart(); $end_timestamp = $this->getShowEnd(); - $instance_id = $this->getShowInstanceId(); - return Schedule::GetPercentScheduled($instance_id, $start_timestamp, $end_timestamp); + $time_filled = $this->getTimeScheduled(); + + $s_epoch = strtotime($start_timestamp); + $e_epoch = strtotime($end_timestamp); + $i_epoch = Schedule::WallTimeToMillisecs($time_filled) / 1000; + + $percent = ceil(($i_epoch / ($e_epoch - $s_epoch)) * 100); + + if ($percent > 100) + $percent = 100; + + return $percent; } public function getShowLength() @@ -1398,8 +1423,7 @@ class ShowInstance { public function searchPlaylistsForShow($datatables) { - $time_remaining = $this->getTimeUnScheduled(); - return StoredFile::searchPlaylistsForSchedule($time_remaining, $datatables); + return StoredFile::searchPlaylistsForSchedule($datatables); } public function getShowListContent() diff --git a/application/models/StoredFile.php b/application/models/StoredFile.php index 957d552b0..06a69ce08 100644 --- a/application/models/StoredFile.php +++ b/application/models/StoredFile.php @@ -1555,17 +1555,15 @@ class StoredFile { } - - public static function searchPlaylistsForSchedule($time_remaining, $datatables) + public static function searchPlaylistsForSchedule($datatables) { $fromTable = "cc_playlist AS pl LEFT JOIN cc_playlisttimes AS plt USING(id) LEFT JOIN cc_subjs AS sub ON pl.editedby = sub.id"; - $datatables["optWhere"][] = "INTERVAL '{$time_remaining}' > INTERVAL '00:00:00'"; + //$datatables["optWhere"][] = "INTERVAL '{$time_remaining}' > INTERVAL '00:00:00'"; $datatables["optWhere"][] = "plt.length > INTERVAL '00:00:00'"; return StoredFile::searchFiles($fromTable, $datatables); } - public static function searchFiles($fromTable, $data) { global $CC_CONFIG, $CC_DBC; diff --git a/application/models/airtime/map/CcScheduleTableMap.php b/application/models/airtime/map/CcScheduleTableMap.php index 92afaab0c..da33e623b 100644 --- a/application/models/airtime/map/CcScheduleTableMap.php +++ b/application/models/airtime/map/CcScheduleTableMap.php @@ -63,4 +63,17 @@ class CcScheduleTableMap extends TableMap { $this->addRelation('CcShowInstances', 'CcShowInstances', RelationMap::MANY_TO_ONE, array('instance_id' => 'id', ), 'CASCADE', null); } // buildRelations() + /** + * + * Gets the list of behaviors registered for this table + * + * @return array Associative array (name => parameters) of behaviors + */ + public function getBehaviors() + { + return array( + 'aggregate_column_relation' => array('foreign_table' => 'cc_show_instances', 'update_method' => 'updateDbTimeFilled', ), + ); + } // getBehaviors() + } // CcScheduleTableMap diff --git a/application/models/airtime/map/CcShowInstancesTableMap.php b/application/models/airtime/map/CcShowInstancesTableMap.php index c69459bc0..d2882172a 100644 --- a/application/models/airtime/map/CcShowInstancesTableMap.php +++ b/application/models/airtime/map/CcShowInstancesTableMap.php @@ -47,6 +47,7 @@ class CcShowInstancesTableMap extends TableMap { $this->addForeignKey('INSTANCE_ID', 'DbOriginalShow', 'INTEGER', 'cc_show_instances', 'ID', false, null, null); $this->addForeignKey('FILE_ID', 'DbRecordedFile', 'INTEGER', 'cc_files', 'ID', false, null, null); $this->addColumn('SOUNDCLOUD_ID', 'DbSoundCloudId', 'INTEGER', false, null, null); + $this->addColumn('TIME_FILLED', 'DbTimeFilled', 'TIME', false, null, null); // validators } // initialize() @@ -62,4 +63,17 @@ class CcShowInstancesTableMap extends TableMap { $this->addRelation('CcSchedule', 'CcSchedule', RelationMap::ONE_TO_MANY, array('id' => 'instance_id', ), 'CASCADE', null); } // buildRelations() + /** + * + * Gets the list of behaviors registered for this table + * + * @return array Associative array (name => parameters) of behaviors + */ + public function getBehaviors() + { + return array( + 'aggregate_column' => array('name' => 'time_filled', 'expression' => 'SUM(clip_length)', 'foreign_table' => 'cc_schedule', ), + ); + } // getBehaviors() + } // CcShowInstancesTableMap diff --git a/application/models/airtime/om/BaseCcSchedule.php b/application/models/airtime/om/BaseCcSchedule.php index 2c8933840..8ae9277a5 100644 --- a/application/models/airtime/om/BaseCcSchedule.php +++ b/application/models/airtime/om/BaseCcSchedule.php @@ -134,6 +134,9 @@ abstract class BaseCcSchedule extends BaseObject implements Persistent */ protected $alreadyInValidation = false; + // aggregate_column_relation behavior + protected $oldCcShowInstances; + /** * Applies default values to this object. * This method should be called from the object's constructor (or @@ -1183,6 +1186,8 @@ abstract class BaseCcSchedule extends BaseObject implements Persistent $this->postUpdate($con); } $this->postSave($con); + // aggregate_column_relation behavior + $this->updateRelatedCcShowInstances($con); CcSchedulePeer::addInstanceToPool($this); } else { $affectedRows = 0; @@ -1710,6 +1715,10 @@ abstract class BaseCcSchedule extends BaseObject implements Persistent */ public function setCcShowInstances(CcShowInstances $v = null) { + // aggregate_column_relation behavior + if (null !== $this->aCcShowInstances && $v !== $this->aCcShowInstances) { + $this->oldCcShowInstances = $this->aCcShowInstances; + } if ($v === null) { $this->setDbInstanceId(NULL); } else { @@ -1795,6 +1804,24 @@ abstract class BaseCcSchedule extends BaseObject implements Persistent $this->aCcShowInstances = null; } + // aggregate_column_relation behavior + + /** + * Update the aggregate column in the related CcShowInstances object + * + * @param PropelPDO $con A connection object + */ + protected function updateRelatedCcShowInstances(PropelPDO $con) + { + if ($ccShowInstances = $this->getCcShowInstances()) { + $ccShowInstances->updateDbTimeFilled($con); + } + if ($this->oldCcShowInstances) { + $this->oldCcShowInstances->updateDbTimeFilled($con); + $this->oldCcShowInstances = null; + } + } + /** * Catches calls to virtual methods */ diff --git a/application/models/airtime/om/BaseCcScheduleQuery.php b/application/models/airtime/om/BaseCcScheduleQuery.php index 73c237c69..5eccb6faa 100644 --- a/application/models/airtime/om/BaseCcScheduleQuery.php +++ b/application/models/airtime/om/BaseCcScheduleQuery.php @@ -657,4 +657,90 @@ abstract class BaseCcScheduleQuery extends ModelCriteria return $this; } + /** + * Code to execute before every DELETE statement + * + * @param PropelPDO $con The connection object used by the query + */ + protected function basePreDelete(PropelPDO $con) + { + // aggregate_column_relation behavior + $this->findRelatedCcShowInstancess($con); + + return $this->preDelete($con); + } + + /** + * Code to execute after every DELETE statement + * + * @param int $affectedRows the number of deleted rows + * @param PropelPDO $con The connection object used by the query + */ + protected function basePostDelete($affectedRows, PropelPDO $con) + { + // aggregate_column_relation behavior + $this->updateRelatedCcShowInstancess($con); + + return $this->postDelete($affectedRows, $con); + } + + /** + * Code to execute before every UPDATE statement + * + * @param array $values The associatiove array of columns and values for the update + * @param PropelPDO $con The connection object used by the query + * @param boolean $forceIndividualSaves If false (default), the resulting call is a BasePeer::doUpdate(), ortherwise it is a series of save() calls on all the found objects + */ + protected function basePreUpdate(&$values, PropelPDO $con, $forceIndividualSaves = false) + { + // aggregate_column_relation behavior + $this->findRelatedCcShowInstancess($con); + + return $this->preUpdate($values, $con, $forceIndividualSaves); + } + + /** + * Code to execute after every UPDATE statement + * + * @param int $affectedRows the number of udated rows + * @param PropelPDO $con The connection object used by the query + */ + protected function basePostUpdate($affectedRows, PropelPDO $con) + { + // aggregate_column_relation behavior + $this->updateRelatedCcShowInstancess($con); + + return $this->postUpdate($affectedRows, $con); + } + + // aggregate_column_relation behavior + + /** + * Finds the related CcShowInstances objects and keep them for later + * + * @param PropelPDO $con A connection object + */ + protected function findRelatedCcShowInstancess($con) + { + $criteria = clone $this; + if ($this->useAliasInSQL) { + $alias = $this->getModelAlias(); + $criteria->removeAlias($alias); + } else { + $alias = ''; + } + $this->ccShowInstancess = CcShowInstancesQuery::create() + ->joinCcSchedule($alias) + ->mergeWith($criteria) + ->find($con); + } + + protected function updateRelatedCcShowInstancess($con) + { + foreach ($this->ccShowInstancess as $ccShowInstances) { + $ccShowInstances->updateDbTimeFilled($con); + } + $this->ccShowInstancess = array(); + } + } // BaseCcScheduleQuery diff --git a/application/models/airtime/om/BaseCcShowInstances.php b/application/models/airtime/om/BaseCcShowInstances.php index 6d608ce19..1e1d90e05 100644 --- a/application/models/airtime/om/BaseCcShowInstances.php +++ b/application/models/airtime/om/BaseCcShowInstances.php @@ -80,6 +80,12 @@ abstract class BaseCcShowInstances extends BaseObject implements Persistent */ protected $soundcloud_id; + /** + * The value for the time_filled field. + * @var string + */ + protected $time_filled; + /** * @var CcShow */ @@ -277,6 +283,39 @@ abstract class BaseCcShowInstances extends BaseObject implements Persistent return $this->soundcloud_id; } + /** + * Get the [optionally formatted] temporal [time_filled] column value. + * + * + * @param string $format The date/time format string (either date()-style or strftime()-style). + * If format is NULL, then the raw DateTime object will be returned. + * @return mixed Formatted date/time value as string or DateTime object (if format is NULL), NULL if column is NULL + * @throws PropelException - if unable to parse/validate the date/time value. + */ + public function getDbTimeFilled($format = '%X') + { + if ($this->time_filled === null) { + return null; + } + + + + try { + $dt = new DateTime($this->time_filled); + } catch (Exception $x) { + throw new PropelException("Internally stored date/time/timestamp value could not be converted to DateTime: " . var_export($this->time_filled, true), $x); + } + + if ($format === null) { + // Because propel.useDateTimeClass is TRUE, we return a DateTime object. + return $dt; + } elseif (strpos($format, '%') !== false) { + return strftime($format, $dt->format('U')); + } else { + return $dt->format($format); + } + } + /** * Set the value of [id] column. * @@ -527,6 +566,55 @@ abstract class BaseCcShowInstances extends BaseObject implements Persistent return $this; } // setDbSoundCloudId() + /** + * Sets the value of [time_filled] column to a normalized version of the date/time value specified. + * + * @param mixed $v string, integer (timestamp), or DateTime value. Empty string will + * be treated as NULL for temporal objects. + * @return CcShowInstances The current object (for fluent API support) + */ + public function setDbTimeFilled($v) + { + // we treat '' as NULL for temporal objects because DateTime('') == DateTime('now') + // -- which is unexpected, to say the least. + if ($v === null || $v === '') { + $dt = null; + } elseif ($v instanceof DateTime) { + $dt = $v; + } else { + // some string/numeric value passed; we normalize that so that we can + // validate it. + try { + if (is_numeric($v)) { // if it's a unix timestamp + $dt = new DateTime('@'.$v, new DateTimeZone('UTC')); + // We have to explicitly specify and then change the time zone because of a + // DateTime bug: http://bugs.php.net/bug.php?id=43003 + $dt->setTimeZone(new DateTimeZone(date_default_timezone_get())); + } else { + $dt = new DateTime($v); + } + } catch (Exception $x) { + throw new PropelException('Error parsing date/time value: ' . var_export($v, true), $x); + } + } + + if ( $this->time_filled !== null || $dt !== null ) { + // (nested ifs are a little easier to read in this case) + + $currNorm = ($this->time_filled !== null && $tmpDt = new DateTime($this->time_filled)) ? $tmpDt->format('H:i:s') : null; + $newNorm = ($dt !== null) ? $dt->format('H:i:s') : null; + + if ( ($currNorm !== $newNorm) // normalized values don't match + ) + { + $this->time_filled = ($dt ? $dt->format('H:i:s') : null); + $this->modifiedColumns[] = CcShowInstancesPeer::TIME_FILLED; + } + } // if either are not null + + return $this; + } // setDbTimeFilled() + /** * Indicates whether the columns in this object are only set to default values. * @@ -576,6 +664,7 @@ abstract class BaseCcShowInstances extends BaseObject implements Persistent $this->instance_id = ($row[$startcol + 6] !== null) ? (int) $row[$startcol + 6] : null; $this->file_id = ($row[$startcol + 7] !== null) ? (int) $row[$startcol + 7] : null; $this->soundcloud_id = ($row[$startcol + 8] !== null) ? (int) $row[$startcol + 8] : null; + $this->time_filled = ($row[$startcol + 9] !== null) ? (string) $row[$startcol + 9] : null; $this->resetModified(); $this->setNew(false); @@ -584,7 +673,7 @@ abstract class BaseCcShowInstances extends BaseObject implements Persistent $this->ensureConsistency(); } - return $startcol + 9; // 9 = CcShowInstancesPeer::NUM_COLUMNS - CcShowInstancesPeer::NUM_LAZY_LOAD_COLUMNS). + return $startcol + 10; // 10 = CcShowInstancesPeer::NUM_COLUMNS - CcShowInstancesPeer::NUM_LAZY_LOAD_COLUMNS). } catch (Exception $e) { throw new PropelException("Error populating CcShowInstances object", $e); @@ -1008,6 +1097,9 @@ abstract class BaseCcShowInstances extends BaseObject implements Persistent case 8: return $this->getDbSoundCloudId(); break; + case 9: + return $this->getDbTimeFilled(); + break; default: return null; break; @@ -1041,6 +1133,7 @@ abstract class BaseCcShowInstances extends BaseObject implements Persistent $keys[6] => $this->getDbOriginalShow(), $keys[7] => $this->getDbRecordedFile(), $keys[8] => $this->getDbSoundCloudId(), + $keys[9] => $this->getDbTimeFilled(), ); if ($includeForeignObjects) { if (null !== $this->aCcShow) { @@ -1110,6 +1203,9 @@ abstract class BaseCcShowInstances extends BaseObject implements Persistent case 8: $this->setDbSoundCloudId($value); break; + case 9: + $this->setDbTimeFilled($value); + break; } // switch() } @@ -1143,6 +1239,7 @@ abstract class BaseCcShowInstances extends BaseObject implements Persistent if (array_key_exists($keys[6], $arr)) $this->setDbOriginalShow($arr[$keys[6]]); if (array_key_exists($keys[7], $arr)) $this->setDbRecordedFile($arr[$keys[7]]); if (array_key_exists($keys[8], $arr)) $this->setDbSoundCloudId($arr[$keys[8]]); + if (array_key_exists($keys[9], $arr)) $this->setDbTimeFilled($arr[$keys[9]]); } /** @@ -1163,6 +1260,7 @@ abstract class BaseCcShowInstances extends BaseObject implements Persistent if ($this->isColumnModified(CcShowInstancesPeer::INSTANCE_ID)) $criteria->add(CcShowInstancesPeer::INSTANCE_ID, $this->instance_id); if ($this->isColumnModified(CcShowInstancesPeer::FILE_ID)) $criteria->add(CcShowInstancesPeer::FILE_ID, $this->file_id); if ($this->isColumnModified(CcShowInstancesPeer::SOUNDCLOUD_ID)) $criteria->add(CcShowInstancesPeer::SOUNDCLOUD_ID, $this->soundcloud_id); + if ($this->isColumnModified(CcShowInstancesPeer::TIME_FILLED)) $criteria->add(CcShowInstancesPeer::TIME_FILLED, $this->time_filled); return $criteria; } @@ -1232,6 +1330,7 @@ abstract class BaseCcShowInstances extends BaseObject implements Persistent $copyObj->setDbOriginalShow($this->instance_id); $copyObj->setDbRecordedFile($this->file_id); $copyObj->setDbSoundCloudId($this->soundcloud_id); + $copyObj->setDbTimeFilled($this->time_filled); if ($deepCopy) { // important: temporarily setNew(false) because this affects the behavior of @@ -1724,6 +1823,7 @@ abstract class BaseCcShowInstances extends BaseObject implements Persistent $this->instance_id = null; $this->file_id = null; $this->soundcloud_id = null; + $this->time_filled = null; $this->alreadyInSave = false; $this->alreadyInValidation = false; $this->clearAllReferences(); @@ -1764,6 +1864,34 @@ abstract class BaseCcShowInstances extends BaseObject implements Persistent $this->aCcFiles = null; } + // aggregate_column behavior + + /** + * Computes the value of the aggregate column time_filled + * + * @param PropelPDO $con A connection object + * + * @return mixed The scalar result from the aggregate query + */ + public function computeDbTimeFilled(PropelPDO $con) + { + $stmt = $con->prepare('SELECT SUM(clip_length) FROM "cc_schedule" WHERE cc_schedule.INSTANCE_ID = :p1'); + $stmt->bindValue(':p1', $this->getDbId()); + $stmt->execute(); + return $stmt->fetchColumn(); + } + + /** + * Updates the aggregate column time_filled + * + * @param PropelPDO $con A connection object + */ + public function updateDbTimeFilled(PropelPDO $con) + { + $this->setDbTimeFilled($this->computeDbTimeFilled($con)); + $this->save($con); + } + /** * Catches calls to virtual methods */ diff --git a/application/models/airtime/om/BaseCcShowInstancesPeer.php b/application/models/airtime/om/BaseCcShowInstancesPeer.php index 78a7e8b83..d7dda7647 100644 --- a/application/models/airtime/om/BaseCcShowInstancesPeer.php +++ b/application/models/airtime/om/BaseCcShowInstancesPeer.php @@ -26,7 +26,7 @@ abstract class BaseCcShowInstancesPeer { const TM_CLASS = 'CcShowInstancesTableMap'; /** The total number of columns. */ - const NUM_COLUMNS = 9; + const NUM_COLUMNS = 10; /** The number of lazy-loaded columns. */ const NUM_LAZY_LOAD_COLUMNS = 0; @@ -58,6 +58,9 @@ abstract class BaseCcShowInstancesPeer { /** the column name for the SOUNDCLOUD_ID field */ const SOUNDCLOUD_ID = 'cc_show_instances.SOUNDCLOUD_ID'; + /** the column name for the TIME_FILLED field */ + const TIME_FILLED = 'cc_show_instances.TIME_FILLED'; + /** * An identiy map to hold any loaded instances of CcShowInstances objects. * This must be public so that other peer classes can access this when hydrating from JOIN @@ -74,12 +77,12 @@ abstract class BaseCcShowInstancesPeer { * e.g. self::$fieldNames[self::TYPE_PHPNAME][0] = 'Id' */ private static $fieldNames = array ( - BasePeer::TYPE_PHPNAME => array ('DbId', 'DbStarts', 'DbEnds', 'DbShowId', 'DbRecord', 'DbRebroadcast', 'DbOriginalShow', 'DbRecordedFile', 'DbSoundCloudId', ), - BasePeer::TYPE_STUDLYPHPNAME => array ('dbId', 'dbStarts', 'dbEnds', 'dbShowId', 'dbRecord', 'dbRebroadcast', 'dbOriginalShow', 'dbRecordedFile', 'dbSoundCloudId', ), - BasePeer::TYPE_COLNAME => array (self::ID, self::STARTS, self::ENDS, self::SHOW_ID, self::RECORD, self::REBROADCAST, self::INSTANCE_ID, self::FILE_ID, self::SOUNDCLOUD_ID, ), - BasePeer::TYPE_RAW_COLNAME => array ('ID', 'STARTS', 'ENDS', 'SHOW_ID', 'RECORD', 'REBROADCAST', 'INSTANCE_ID', 'FILE_ID', 'SOUNDCLOUD_ID', ), - BasePeer::TYPE_FIELDNAME => array ('id', 'starts', 'ends', 'show_id', 'record', 'rebroadcast', 'instance_id', 'file_id', 'soundcloud_id', ), - BasePeer::TYPE_NUM => array (0, 1, 2, 3, 4, 5, 6, 7, 8, ) + BasePeer::TYPE_PHPNAME => array ('DbId', 'DbStarts', 'DbEnds', 'DbShowId', 'DbRecord', 'DbRebroadcast', 'DbOriginalShow', 'DbRecordedFile', 'DbSoundCloudId', 'DbTimeFilled', ), + BasePeer::TYPE_STUDLYPHPNAME => array ('dbId', 'dbStarts', 'dbEnds', 'dbShowId', 'dbRecord', 'dbRebroadcast', 'dbOriginalShow', 'dbRecordedFile', 'dbSoundCloudId', 'dbTimeFilled', ), + BasePeer::TYPE_COLNAME => array (self::ID, self::STARTS, self::ENDS, self::SHOW_ID, self::RECORD, self::REBROADCAST, self::INSTANCE_ID, self::FILE_ID, self::SOUNDCLOUD_ID, self::TIME_FILLED, ), + BasePeer::TYPE_RAW_COLNAME => array ('ID', 'STARTS', 'ENDS', 'SHOW_ID', 'RECORD', 'REBROADCAST', 'INSTANCE_ID', 'FILE_ID', 'SOUNDCLOUD_ID', 'TIME_FILLED', ), + BasePeer::TYPE_FIELDNAME => array ('id', 'starts', 'ends', 'show_id', 'record', 'rebroadcast', 'instance_id', 'file_id', 'soundcloud_id', 'time_filled', ), + BasePeer::TYPE_NUM => array (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, ) ); /** @@ -89,12 +92,12 @@ abstract class BaseCcShowInstancesPeer { * e.g. self::$fieldNames[BasePeer::TYPE_PHPNAME]['Id'] = 0 */ private static $fieldKeys = array ( - BasePeer::TYPE_PHPNAME => array ('DbId' => 0, 'DbStarts' => 1, 'DbEnds' => 2, 'DbShowId' => 3, 'DbRecord' => 4, 'DbRebroadcast' => 5, 'DbOriginalShow' => 6, 'DbRecordedFile' => 7, 'DbSoundCloudId' => 8, ), - BasePeer::TYPE_STUDLYPHPNAME => array ('dbId' => 0, 'dbStarts' => 1, 'dbEnds' => 2, 'dbShowId' => 3, 'dbRecord' => 4, 'dbRebroadcast' => 5, 'dbOriginalShow' => 6, 'dbRecordedFile' => 7, 'dbSoundCloudId' => 8, ), - BasePeer::TYPE_COLNAME => array (self::ID => 0, self::STARTS => 1, self::ENDS => 2, self::SHOW_ID => 3, self::RECORD => 4, self::REBROADCAST => 5, self::INSTANCE_ID => 6, self::FILE_ID => 7, self::SOUNDCLOUD_ID => 8, ), - BasePeer::TYPE_RAW_COLNAME => array ('ID' => 0, 'STARTS' => 1, 'ENDS' => 2, 'SHOW_ID' => 3, 'RECORD' => 4, 'REBROADCAST' => 5, 'INSTANCE_ID' => 6, 'FILE_ID' => 7, 'SOUNDCLOUD_ID' => 8, ), - BasePeer::TYPE_FIELDNAME => array ('id' => 0, 'starts' => 1, 'ends' => 2, 'show_id' => 3, 'record' => 4, 'rebroadcast' => 5, 'instance_id' => 6, 'file_id' => 7, 'soundcloud_id' => 8, ), - BasePeer::TYPE_NUM => array (0, 1, 2, 3, 4, 5, 6, 7, 8, ) + BasePeer::TYPE_PHPNAME => array ('DbId' => 0, 'DbStarts' => 1, 'DbEnds' => 2, 'DbShowId' => 3, 'DbRecord' => 4, 'DbRebroadcast' => 5, 'DbOriginalShow' => 6, 'DbRecordedFile' => 7, 'DbSoundCloudId' => 8, 'DbTimeFilled' => 9, ), + BasePeer::TYPE_STUDLYPHPNAME => array ('dbId' => 0, 'dbStarts' => 1, 'dbEnds' => 2, 'dbShowId' => 3, 'dbRecord' => 4, 'dbRebroadcast' => 5, 'dbOriginalShow' => 6, 'dbRecordedFile' => 7, 'dbSoundCloudId' => 8, 'dbTimeFilled' => 9, ), + BasePeer::TYPE_COLNAME => array (self::ID => 0, self::STARTS => 1, self::ENDS => 2, self::SHOW_ID => 3, self::RECORD => 4, self::REBROADCAST => 5, self::INSTANCE_ID => 6, self::FILE_ID => 7, self::SOUNDCLOUD_ID => 8, self::TIME_FILLED => 9, ), + BasePeer::TYPE_RAW_COLNAME => array ('ID' => 0, 'STARTS' => 1, 'ENDS' => 2, 'SHOW_ID' => 3, 'RECORD' => 4, 'REBROADCAST' => 5, 'INSTANCE_ID' => 6, 'FILE_ID' => 7, 'SOUNDCLOUD_ID' => 8, 'TIME_FILLED' => 9, ), + BasePeer::TYPE_FIELDNAME => array ('id' => 0, 'starts' => 1, 'ends' => 2, 'show_id' => 3, 'record' => 4, 'rebroadcast' => 5, 'instance_id' => 6, 'file_id' => 7, 'soundcloud_id' => 8, 'time_filled' => 9, ), + BasePeer::TYPE_NUM => array (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, ) ); /** @@ -175,6 +178,7 @@ abstract class BaseCcShowInstancesPeer { $criteria->addSelectColumn(CcShowInstancesPeer::INSTANCE_ID); $criteria->addSelectColumn(CcShowInstancesPeer::FILE_ID); $criteria->addSelectColumn(CcShowInstancesPeer::SOUNDCLOUD_ID); + $criteria->addSelectColumn(CcShowInstancesPeer::TIME_FILLED); } else { $criteria->addSelectColumn($alias . '.ID'); $criteria->addSelectColumn($alias . '.STARTS'); @@ -185,6 +189,7 @@ abstract class BaseCcShowInstancesPeer { $criteria->addSelectColumn($alias . '.INSTANCE_ID'); $criteria->addSelectColumn($alias . '.FILE_ID'); $criteria->addSelectColumn($alias . '.SOUNDCLOUD_ID'); + $criteria->addSelectColumn($alias . '.TIME_FILLED'); } } diff --git a/application/models/airtime/om/BaseCcShowInstancesQuery.php b/application/models/airtime/om/BaseCcShowInstancesQuery.php index a45f511cc..3d49926a9 100644 --- a/application/models/airtime/om/BaseCcShowInstancesQuery.php +++ b/application/models/airtime/om/BaseCcShowInstancesQuery.php @@ -15,6 +15,7 @@ * @method CcShowInstancesQuery orderByDbOriginalShow($order = Criteria::ASC) Order by the instance_id column * @method CcShowInstancesQuery orderByDbRecordedFile($order = Criteria::ASC) Order by the file_id column * @method CcShowInstancesQuery orderByDbSoundCloudId($order = Criteria::ASC) Order by the soundcloud_id column + * @method CcShowInstancesQuery orderByDbTimeFilled($order = Criteria::ASC) Order by the time_filled column * * @method CcShowInstancesQuery groupByDbId() Group by the id column * @method CcShowInstancesQuery groupByDbStarts() Group by the starts column @@ -25,6 +26,7 @@ * @method CcShowInstancesQuery groupByDbOriginalShow() Group by the instance_id column * @method CcShowInstancesQuery groupByDbRecordedFile() Group by the file_id column * @method CcShowInstancesQuery groupByDbSoundCloudId() Group by the soundcloud_id column + * @method CcShowInstancesQuery groupByDbTimeFilled() Group by the time_filled column * * @method CcShowInstancesQuery leftJoin($relation) Adds a LEFT JOIN clause to the query * @method CcShowInstancesQuery rightJoin($relation) Adds a RIGHT JOIN clause to the query @@ -62,6 +64,7 @@ * @method CcShowInstances findOneByDbOriginalShow(int $instance_id) Return the first CcShowInstances filtered by the instance_id column * @method CcShowInstances findOneByDbRecordedFile(int $file_id) Return the first CcShowInstances filtered by the file_id column * @method CcShowInstances findOneByDbSoundCloudId(int $soundcloud_id) Return the first CcShowInstances filtered by the soundcloud_id column + * @method CcShowInstances findOneByDbTimeFilled(string $time_filled) Return the first CcShowInstances filtered by the time_filled column * * @method array findByDbId(int $id) Return CcShowInstances objects filtered by the id column * @method array findByDbStarts(string $starts) Return CcShowInstances objects filtered by the starts column @@ -72,6 +75,7 @@ * @method array findByDbOriginalShow(int $instance_id) Return CcShowInstances objects filtered by the instance_id column * @method array findByDbRecordedFile(int $file_id) Return CcShowInstances objects filtered by the file_id column * @method array findByDbSoundCloudId(int $soundcloud_id) Return CcShowInstances objects filtered by the soundcloud_id column + * @method array findByDbTimeFilled(string $time_filled) Return CcShowInstances objects filtered by the time_filled column * * @package propel.generator.airtime.om */ @@ -446,6 +450,37 @@ abstract class BaseCcShowInstancesQuery extends ModelCriteria return $this->addUsingAlias(CcShowInstancesPeer::SOUNDCLOUD_ID, $dbSoundCloudId, $comparison); } + /** + * Filter the query on the time_filled column + * + * @param string|array $dbTimeFilled The value to use as filter. + * Accepts an associative array('min' => $minValue, 'max' => $maxValue) + * @param string $comparison Operator to use for the column comparison, defaults to Criteria::EQUAL + * + * @return CcShowInstancesQuery The current query, for fluid interface + */ + public function filterByDbTimeFilled($dbTimeFilled = null, $comparison = null) + { + if (is_array($dbTimeFilled)) { + $useMinMax = false; + if (isset($dbTimeFilled['min'])) { + $this->addUsingAlias(CcShowInstancesPeer::TIME_FILLED, $dbTimeFilled['min'], Criteria::GREATER_EQUAL); + $useMinMax = true; + } + if (isset($dbTimeFilled['max'])) { + $this->addUsingAlias(CcShowInstancesPeer::TIME_FILLED, $dbTimeFilled['max'], Criteria::LESS_EQUAL); + $useMinMax = true; + } + if ($useMinMax) { + return $this; + } + if (null === $comparison) { + $comparison = Criteria::IN; + } + } + return $this->addUsingAlias(CcShowInstancesPeer::TIME_FILLED, $dbTimeFilled, $comparison); + } + /** * Filter the query by a related CcShow object * diff --git a/application/models/tests/populator.php b/application/models/tests/populator.php index 4c3b5a994..2edc0df93 100644 --- a/application/models/tests/populator.php +++ b/application/models/tests/populator.php @@ -58,6 +58,7 @@ function createTestShow($showNumber, $showTime, $duration = "1:00") $data['add_show_description'] = 'automated show'; $data['add_show_url'] = 'http://www.OfirGal.com'; $data['add_show_color'] = ""; + $data['add_show_genre'] = "Ofir"; $data['add_show_background_color'] = ""; $data['add_show_record'] = 0; $data['add_show_hosts'] =""; @@ -76,10 +77,10 @@ function createTestShow($showNumber, $showTime, $duration = "1:00") $showTime = new DateTime(); -$resolution = "minute"; +$resolution = "hour"; $showNumber = 1; -$numberOfDays = 0; -$numberOfHours = 1; +$numberOfDays = 180; +$numberOfHours = 0; $endDate = new DateTime(); $endDate->add(new DateInterval("P".$numberOfDays."DT".$numberOfHours."H")); echo "End date: ".$endDate->format("Y-m-d H:i")."\n"; diff --git a/build/schema.xml b/build/schema.xml index ac1151636..1546e3e0c 100644 --- a/build/schema.xml +++ b/build/schema.xml @@ -133,6 +133,12 @@ + + + + + + diff --git a/build/sql/schema.sql b/build/sql/schema.sql index 6231f20e5..8cf1d3a73 100644 --- a/build/sql/schema.sql +++ b/build/sql/schema.sql @@ -171,6 +171,7 @@ CREATE TABLE "cc_show_instances" "instance_id" INTEGER, "file_id" INTEGER, "soundcloud_id" INTEGER, + "time_filled" TIME, PRIMARY KEY ("id") ); diff --git a/install/DoctrineMigrations/Version20110406182005.php b/install/DoctrineMigrations/Version20110406182005.php new file mode 100644 index 000000000..671b32c17 --- /dev/null +++ b/install/DoctrineMigrations/Version20110406182005.php @@ -0,0 +1,47 @@ +getTable('cc_show_instances'); + + $cc_show_instances->addColumn('time_filled', 'time', array('notnull' => false)); + //end cc_show_instances modifications + + //start cc_show_rebroadcast modifications + $cc_show_rebroadcast = $schema->getTable('cc_show_rebroadcast'); + + $type = $cc_show_rebroadcast->getColumn('start_time')->getType()->getName(); + if($type == 'datetime') { + $cc_show_rebroadcast->dropColumn('start_time'); + $cc_show_rebroadcast->addColumn('start_time', 'time', array('notnull' => true)); + } + //end cc_show_rebroadcast modifications + } + + public function down(Schema $schema) + { + //start cc_show_instances modifications + $cc_show_instances = $schema->getTable('cc_show_instances'); + + $cc_show_instances->dropColumn('time_filled'); + //end cc_show_instances modifications + + //start cc_show_rebroadcast modifications + $cc_show_rebroadcast = $schema->getTable('cc_show_rebroadcast'); + + $type = $cc_show_rebroadcast->getColumn('start_time')->getType()->getName(); + if($type == 'datetime') { + $cc_show_rebroadcast->dropColumn('start_time'); + $cc_show_rebroadcast->addColumn('start_time', 'datetime', array('notnull' => 1)); + } + //end cc_show_rebroadcast modifications + } +} diff --git a/install/airtime-uninstall.php b/install/airtime-uninstall.php index 7accac35f..298d3c945 100644 --- a/install/airtime-uninstall.php +++ b/install/airtime-uninstall.php @@ -9,6 +9,11 @@ require_once(dirname(__FILE__).'/include/AirtimeIni.php'); // Need to check that we are superuser before running this. AirtimeIni::ExitIfNotRoot(); +if (!file_exists('/etc/airtime/airtime.conf')) { + echo PHP_EOL."Airtime config file '/etc/airtime/airtime.conf' does not exist.".PHP_EOL; + echo "Most likely this means that Airtime is not installed, so there is nothing to do.".PHP_EOL.PHP_EOL; + exit(); +} require_once(dirname(__FILE__).'/../application/configs/conf.php'); require_once(dirname(__FILE__).'/include/AirtimeInstall.php'); @@ -39,7 +44,7 @@ if ($dbDeleteFailed) { if (!PEAR::isError($CC_DBC)) { $sql = "select * from pg_tables where tableowner = 'airtime'"; $rows = $CC_DBC->GetAll($sql); - if (PEAR::isError($result)) { + if (PEAR::isError($rows)) { $rows = array(); } diff --git a/install/airtime-upgrade.php b/install/airtime-upgrade.php index a84617321..799cb4b76 100644 --- a/install/airtime-upgrade.php +++ b/install/airtime-upgrade.php @@ -7,22 +7,13 @@ */ require_once(dirname(__FILE__).'/include/AirtimeIni.php'); -require_once(dirname(__FILE__).'/include/AirtimeInstall.php'); AirtimeIni::ExitIfNotRoot(); echo "******************************** Update Begin *********************************".PHP_EOL; -AirtimeIni::CreateIniFile(); -AirtimeIni::UpdateIniFiles(); -echo PHP_EOL."*** Updating Database Tables ***".PHP_EOL; -AirtimeInstall::MigrateTables(__DIR__); - -echo PHP_EOL."*** Updating Pypo ***".PHP_EOL; -system("python ".__DIR__."/../python_apps/pypo/install/pypo-install.py"); - -echo PHP_EOL."*** Recorder Installation ***".PHP_EOL; -system("python ".__DIR__."/../python_apps/show-recorder/install/recorder-install.py"); +//system("php ".__DIR__."/upgrades/airtime-1.7/airtime-upgrade.php"); +system("php ".__DIR__."/upgrades/airtime-1.8/airtime-upgrade.php"); echo "******************************* Update Complete *******************************".PHP_EOL; diff --git a/install/include/AirtimeInstall.php b/install/include/AirtimeInstall.php index bd3a568c2..ddac4bd06 100644 --- a/install/include/AirtimeInstall.php +++ b/install/include/AirtimeInstall.php @@ -147,9 +147,21 @@ class AirtimeInstall { @exec($command, $output, $results); } - public static function MigrateTables($dir) + public static function BypassMigrations($dir, $version) { - $command = "php $dir/../library/doctrine/migrations/doctrine-migrations.phar --configuration=$dir/DoctrineMigrations/migrations.xml --db-configuration=$dir/../library/doctrine/migrations/migrations-db.php --no-interaction migrations:migrate"; + $command = "php $dir/../../../library/doctrine/migrations/doctrine-migrations.phar ". + "--configuration=$dir/../../DoctrineMigrations/migrations.xml ". + "--db-configuration=$dir/../../../library/doctrine/migrations/migrations-db.php ". + "--no-interaction --add migrations:version $version"; + system($command); + } + + public static function MigrateTablesToVersion($dir, $version) + { + $command = "php $dir/../../../library/doctrine/migrations/doctrine-migrations.phar ". + "--configuration=$dir/../../DoctrineMigrations/migrations.xml ". + "--db-configuration=$dir/../../../library/doctrine/migrations/migrations-db.php ". + "--no-interaction migrations:migrate $version"; system($command); } diff --git a/install/upgrades/airtime-1.7/airtime-upgrade.php b/install/upgrades/airtime-1.7/airtime-upgrade.php new file mode 100644 index 000000000..4052ce4b2 --- /dev/null +++ b/install/upgrades/airtime-1.7/airtime-upgrade.php @@ -0,0 +1,23 @@ +GetAll($sql); + +foreach ($show_instances as $show_instance) { + $sql = "UPDATE cc_show_instances SET time_filled = (SELECT SUM(clip_length) FROM cc_schedule WHERE instance_id = {$show_instance["id"]}) WHERE id = {$show_instance["id"]}"; + $CC_DBC->query($sql); +} +//end setting data for new aggregate show length column. + + diff --git a/public/js/airtime/schedule/schedule.js b/public/js/airtime/schedule/schedule.js index 34644630f..9561785a3 100644 --- a/public/js/airtime/schedule/schedule.js +++ b/public/js/airtime/schedule/schedule.js @@ -256,6 +256,7 @@ $(window).load(function() { }, contentHeight: mainHeight, theme: true, + lazyFetching: false, events: getFullCalendarEvents,