168513a70bcd92384395513322f1b801e7bf9c729Steve Block# Copyright (C) 2010 Google Inc. All rights reserved. 268513a70bcd92384395513322f1b801e7bf9c729Steve Block# 368513a70bcd92384395513322f1b801e7bf9c729Steve Block# Redistribution and use in source and binary forms, with or without 468513a70bcd92384395513322f1b801e7bf9c729Steve Block# modification, are permitted provided that the following conditions are 568513a70bcd92384395513322f1b801e7bf9c729Steve Block# met: 668513a70bcd92384395513322f1b801e7bf9c729Steve Block# 768513a70bcd92384395513322f1b801e7bf9c729Steve Block# * Redistributions of source code must retain the above copyright 868513a70bcd92384395513322f1b801e7bf9c729Steve Block# notice, this list of conditions and the following disclaimer. 968513a70bcd92384395513322f1b801e7bf9c729Steve Block# * Redistributions in binary form must reproduce the above 1068513a70bcd92384395513322f1b801e7bf9c729Steve Block# copyright notice, this list of conditions and the following disclaimer 1168513a70bcd92384395513322f1b801e7bf9c729Steve Block# in the documentation and/or other materials provided with the 1268513a70bcd92384395513322f1b801e7bf9c729Steve Block# distribution. 1368513a70bcd92384395513322f1b801e7bf9c729Steve Block# * Neither the name of Google Inc. nor the names of its 1468513a70bcd92384395513322f1b801e7bf9c729Steve Block# contributors may be used to endorse or promote products derived from 1568513a70bcd92384395513322f1b801e7bf9c729Steve Block# this software without specific prior written permission. 1668513a70bcd92384395513322f1b801e7bf9c729Steve Block# 1768513a70bcd92384395513322f1b801e7bf9c729Steve Block# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 1868513a70bcd92384395513322f1b801e7bf9c729Steve Block# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 1968513a70bcd92384395513322f1b801e7bf9c729Steve Block# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 2068513a70bcd92384395513322f1b801e7bf9c729Steve Block# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 2168513a70bcd92384395513322f1b801e7bf9c729Steve Block# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 2268513a70bcd92384395513322f1b801e7bf9c729Steve Block# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 2368513a70bcd92384395513322f1b801e7bf9c729Steve Block# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 2468513a70bcd92384395513322f1b801e7bf9c729Steve Block# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 2568513a70bcd92384395513322f1b801e7bf9c729Steve Block# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 2668513a70bcd92384395513322f1b801e7bf9c729Steve Block# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 2768513a70bcd92384395513322f1b801e7bf9c729Steve Block# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 2868513a70bcd92384395513322f1b801e7bf9c729Steve Block 2968513a70bcd92384395513322f1b801e7bf9c729Steve Blockfrom google.appengine.ext import db 3068513a70bcd92384395513322f1b801e7bf9c729Steve Block 31bec39347bb3bb5bf1187ccaf471d26247f28b585Kristian Monsenfrom datetime import timedelta, datetime 3268513a70bcd92384395513322f1b801e7bf9c729Steve Blockimport time 3368513a70bcd92384395513322f1b801e7bf9c729Steve Block 34a94275402997c11dd2e778633dacf4b7e630a35dBen Murdochfrom model.queuepropertymixin import QueuePropertyMixin 3568513a70bcd92384395513322f1b801e7bf9c729Steve Block 36a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch 37a94275402997c11dd2e778633dacf4b7e630a35dBen Murdochclass ActiveWorkItems(db.Model, QueuePropertyMixin): 3868513a70bcd92384395513322f1b801e7bf9c729Steve Block queue_name = db.StringProperty() 3968513a70bcd92384395513322f1b801e7bf9c729Steve Block item_ids = db.ListProperty(int) 4068513a70bcd92384395513322f1b801e7bf9c729Steve Block item_dates = db.ListProperty(float) 4168513a70bcd92384395513322f1b801e7bf9c729Steve Block date = db.DateTimeProperty(auto_now_add=True) 4268513a70bcd92384395513322f1b801e7bf9c729Steve Block 43bec39347bb3bb5bf1187ccaf471d26247f28b585Kristian Monsen # The id/date pairs should probably just be their own class. 44bec39347bb3bb5bf1187ccaf471d26247f28b585Kristian Monsen def _item_time_pairs(self): 45bec39347bb3bb5bf1187ccaf471d26247f28b585Kristian Monsen return zip(self.item_ids, self.item_dates) 46bec39347bb3bb5bf1187ccaf471d26247f28b585Kristian Monsen 47bec39347bb3bb5bf1187ccaf471d26247f28b585Kristian Monsen def _set_item_time_pairs(self, pairs): 48bec39347bb3bb5bf1187ccaf471d26247f28b585Kristian Monsen if pairs: 49bec39347bb3bb5bf1187ccaf471d26247f28b585Kristian Monsen # The * operator raises on an empty list. 50bec39347bb3bb5bf1187ccaf471d26247f28b585Kristian Monsen # db.Model does not tuples, we have to make lists. 51bec39347bb3bb5bf1187ccaf471d26247f28b585Kristian Monsen self.item_ids, self.item_dates = map(list, zip(*pairs)) 52bec39347bb3bb5bf1187ccaf471d26247f28b585Kristian Monsen else: 53bec39347bb3bb5bf1187ccaf471d26247f28b585Kristian Monsen self.item_ids = [] 54bec39347bb3bb5bf1187ccaf471d26247f28b585Kristian Monsen self.item_dates = [] 55bec39347bb3bb5bf1187ccaf471d26247f28b585Kristian Monsen 56bec39347bb3bb5bf1187ccaf471d26247f28b585Kristian Monsen def _append_item_time_pair(self, pair): 57bec39347bb3bb5bf1187ccaf471d26247f28b585Kristian Monsen self.item_ids.append(pair[0]) 58bec39347bb3bb5bf1187ccaf471d26247f28b585Kristian Monsen self.item_dates.append(pair[1]) 59bec39347bb3bb5bf1187ccaf471d26247f28b585Kristian Monsen 60a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch def _remove_item(self, item_id): 61bec39347bb3bb5bf1187ccaf471d26247f28b585Kristian Monsen nonexpired_pairs = [pair for pair in self._item_time_pairs() if pair[0] != item_id] 62bec39347bb3bb5bf1187ccaf471d26247f28b585Kristian Monsen self._set_item_time_pairs(nonexpired_pairs) 63bec39347bb3bb5bf1187ccaf471d26247f28b585Kristian Monsen 642daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch @classmethod 652daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch def key_for_queue(cls, queue_name): 662daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch return "active-work-items-%s" % (queue_name) 672daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch 682daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch @classmethod 692daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch def lookup_by_queue(cls, queue_name): 702daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch return cls.get_or_insert(key_name=cls.key_for_queue(queue_name), queue_name=queue_name) 712daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch 72a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch @staticmethod 73a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch def _expire_item(key, item_id): 74a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch active_work_items = db.get(key) 75a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch active_work_items._remove_item(item_id) 76a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch active_work_items.put() 77a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch 78a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch def expire_item(self, item_id): 79a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch return db.run_in_transaction(self._expire_item, self.key(), item_id) 80a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch 8168513a70bcd92384395513322f1b801e7bf9c729Steve Block def deactivate_expired(self, now): 8268513a70bcd92384395513322f1b801e7bf9c729Steve Block one_hour_ago = time.mktime((now - timedelta(minutes=60)).timetuple()) 83bec39347bb3bb5bf1187ccaf471d26247f28b585Kristian Monsen nonexpired_pairs = [pair for pair in self._item_time_pairs() if pair[1] > one_hour_ago] 84bec39347bb3bb5bf1187ccaf471d26247f28b585Kristian Monsen self._set_item_time_pairs(nonexpired_pairs) 8568513a70bcd92384395513322f1b801e7bf9c729Steve Block 8668513a70bcd92384395513322f1b801e7bf9c729Steve Block def next_item(self, work_item_ids, now): 8768513a70bcd92384395513322f1b801e7bf9c729Steve Block for item_id in work_item_ids: 8868513a70bcd92384395513322f1b801e7bf9c729Steve Block if item_id not in self.item_ids: 89bec39347bb3bb5bf1187ccaf471d26247f28b585Kristian Monsen self._append_item_time_pair([item_id, time.mktime(now.timetuple())]) 9068513a70bcd92384395513322f1b801e7bf9c729Steve Block return item_id 9168513a70bcd92384395513322f1b801e7bf9c729Steve Block return None 92bec39347bb3bb5bf1187ccaf471d26247f28b585Kristian Monsen 93bec39347bb3bb5bf1187ccaf471d26247f28b585Kristian Monsen def time_for_item(self, item_id): 94bec39347bb3bb5bf1187ccaf471d26247f28b585Kristian Monsen for active_item_id, time in self._item_time_pairs(): 95bec39347bb3bb5bf1187ccaf471d26247f28b585Kristian Monsen if active_item_id == item_id: 96bec39347bb3bb5bf1187ccaf471d26247f28b585Kristian Monsen return datetime.fromtimestamp(time) 97bec39347bb3bb5bf1187ccaf471d26247f28b585Kristian Monsen return None 98