<s> import re <EOL> from datetime import datetime <EOL> from dateutil . relativedelta import relativedelta <EOL> from breezeminder . app import app <EOL> from breezeminder . models . base import ( BaseDocument , <EOL> BaseQuerySet ) <EOL> class ReminderQuerySet ( BaseQuerySet ) : <EOL> pass <EOL> class ReminderHistoryQuerySet ( BaseQuerySet ) : <EOL> def last_reminder ( self , reminder , card ) : <EOL> try : <EOL> return self . filter ( reminder = reminder , card = card ) [ <NUM_LIT:0> ] <EOL> except IndexError : <EOL> return None <EOL> class ReminderTypeQuerySet ( BaseQuerySet ) : <EOL> @ app . cache . memoize ( timeout = <NUM_LIT> ) <EOL> def all_tuples ( self , field = '<STR_LIT:name>' ) : <EOL> return [ ( t . key , getattr ( t , field ) ) for t in self . all ( ) ] <EOL> class ReminderType ( BaseDocument ) : <EOL> key = app . db . StringField ( required = True ) <EOL> name = app . db . StringField ( required = True ) <EOL> description = app . db . StringField ( ) <EOL> order = app . db . IntField ( required = True , default = <NUM_LIT:0> ) <EOL> meta = { <EOL> '<STR_LIT>' : '<STR_LIT>' , <EOL> '<STR_LIT>' : ReminderTypeQuerySet , <EOL> '<STR_LIT>' : [ '<STR_LIT>' ] , <EOL> '<STR_LIT>' : [ <EOL> { <EOL> '<STR_LIT>' : [ '<STR_LIT:key>' ] , <EOL> '<STR_LIT>' : True <EOL> } <EOL> ] <EOL> } <EOL> def __str__ ( self ) : <EOL> return self . key <EOL> def __eq__ ( self , obj ) : <EOL> if isinstance ( obj , basestring ) : <EOL> return self . key == obj <EOL> else : <EOL> return self == obj <EOL> class Reminder ( BaseDocument ) : <EOL> owner = app . db . ReferenceField ( '<STR_LIT>' , required = True , dbref = False , <EOL> reverse_delete_rule = app . db . CASCADE ) <EOL> type = app . db . ReferenceField ( '<STR_LIT>' , required = True , dbref = False ) <EOL> threshold = app . db . DecimalField ( required = True , default = <NUM_LIT:0.0> ) <EOL> quantifier = app . db . StringField ( ) <EOL> valid_until = app . db . DateTimeField ( ) <EOL> send_email = app . db . BooleanField ( required = True , default = True ) <EOL> send_sms = app . db . BooleanField ( required = True , default = True ) <EOL> meta = { <EOL> '<STR_LIT>' : '<STR_LIT>' , <EOL> '<STR_LIT>' : ReminderQuerySet , <EOL> '<STR_LIT>' : [ '<STR_LIT:type>' , '<STR_LIT>' ] <EOL> } <EOL> def __init__ ( self , ** kwargs ) : <EOL> if '<STR_LIT:type>' in kwargs and isinstance ( kwargs [ '<STR_LIT:type>' ] , basestring ) : <EOL> kwargs [ '<STR_LIT:type>' ] = ReminderType . objects . get ( key = kwargs [ '<STR_LIT:type>' ] ) <EOL> super ( Reminder , self ) . __init__ ( ** kwargs ) <EOL> def last_reminded ( self , card = None ) : <EOL> """<STR_LIT>""" <EOL> if card : <EOL> return ReminderHistory . objects . last_reminder ( self , card ) <EOL> else : <EOL> from breezeminder . models . card import BreezeCard <EOL> reminders = [ ] <EOL> for card in BreezeCard . objects . filter ( owner = self . owner ) : <EOL> reminder = self . last_reminded ( card ) <EOL> if reminder : <EOL> reminders . append ( reminder ) <EOL> return reminders <EOL> def description ( self , html = True ) : <EOL> """<STR_LIT>""" <EOL> if self . type == '<STR_LIT>' : <EOL> action = '<STR_LIT>' <EOL> quantity = '<STR_LIT>' % ( int ( self . threshold ) , self . quantifier ) <EOL> elif self . type . key . startswith ( '<STR_LIT>' ) : <EOL> action = '<STR_LIT>' <EOL> quantity = '<STR_LIT>' <EOL> else : <EOL> fmt = '<STR_LIT>' if self . type == '<STR_LIT>' else '<STR_LIT:%s>' <EOL> action = '<STR_LIT>' <EOL> quantity = fmt % self . threshold <EOL> if html : <EOL> format = '<STR_LIT>' <EOL> else : <EOL> format = '<STR_LIT>' <EOL> return format % ( self . type . name , action , quantity ) <EOL> def check_reminder ( self , card , last_state = None ) : <EOL> """<STR_LIT>""" <EOL> if self . valid_until and datetime . now ( ) >= self . valid_until : <EOL> return False <EOL> if self . type == '<STR_LIT>' : <EOL> return self . _check_balance_reminder ( card , last_state ) <EOL> elif self . type == '<STR_LIT>' : <EOL> return self . _check_ride_reminder ( card , last_state ) <EOL> elif self . type == '<STR_LIT>' : <EOL> return self . _check_round_trip_reminder ( card , last_state ) <EOL> elif self . type == '<STR_LIT>' : <EOL> return self . _check_expiration_reminder ( card ) <EOL> elif self . type == '<STR_LIT>' : <EOL> if last_state : <EOL> return self . _check_available_balance_reminder ( card , last_state ) <EOL> elif self . type == '<STR_LIT>' : <EOL> if last_state : <EOL> return self . _check_available_product_reminder ( card , last_state ) <EOL> else : <EOL> message = '<STR_LIT>' % self . type <EOL> app . logger . exception ( message ) <EOL> raise NotImplementedError ( message ) <EOL> def _check_available_balance_reminder ( self , card , last_state ) : <EOL> """<STR_LIT>""" <EOL> if card . stored_value is not None and last_state . stored_value is not None : <EOL> try : <EOL> if float ( card . stored_value ) > float ( last_state . stored_value ) : <EOL> return True <EOL> except : <EOL> app . logger . exception ( '<STR_LIT>' ) <EOL> pass <EOL> return False <EOL> def _check_available_product_reminder ( self , card , last_state ) : <EOL> """<STR_LIT>""" <EOL> current_prod = card . products if card . products else [ ] <EOL> old_prod = last_state . products if last_state . products else [ ] <EOL> for product in current_prod : <EOL> if product not in old_prod : <EOL> return True <EOL> current_pending = card . pending if card . pending else [ ] <EOL> old_pending = last_state . pending if last_state . pending else [ ] <EOL> for pending in current_pending : <EOL> if pending in old_pending : <EOL> return True <EOL> return False <EOL> def _check_balance_reminder ( self , card , last_state = None ) : <EOL> """<STR_LIT>""" <EOL> if card . stored_value is not None : <EOL> try : <EOL> current_value = float ( card . stored_value ) <EOL> if last_state is not None and last_state . stored_value is not None : <EOL> last_value = float ( last_state . stored_value ) <EOL> if current_value == last_value : <EOL> app . logger . info ( '<STR_LIT>' % ( current_value , card . last_four ) ) <EOL> return False <EOL> return current_value > <NUM_LIT:0> and current_value <= float ( self . threshold ) <EOL> except : <EOL> app . logger . exception ( '<STR_LIT>' ) <EOL> pass <EOL> return False <EOL> def _exp_delta ( self ) : <EOL> try : <EOL> return relativedelta ( ** { self . quantifier . lower ( ) : int ( self . threshold ) } ) <EOL> except : <EOL> message = '<STR_LIT>' % self . quantifier <EOL> app . logger . exception ( message ) <EOL> raise NotImplementedError ( message ) <EOL> def _will_expire ( self , exp_date , delta ) : <EOL> now = datetime . now ( ) . date ( ) <EOL> return now + delta == exp_date . date ( ) and exp_date . date ( ) >= now <EOL> def _check_expiration_reminder ( self , card ) : <EOL> """<STR_LIT>""" <EOL> remind = False <EOL> delta = self . _exp_delta ( ) <EOL> if card . expiration_date is not None : <EOL> remind = remind or self . _will_expire ( card . expiration_date , delta ) <EOL> for product in card . products : <EOL> if product . expiration_date is not None : <EOL> remind = remind or self . _will_expire ( product . expiration_date , delta ) <EOL> return remind <EOL> def _check_round_trip_reminder ( self , card , last_state = None ) : <EOL> """<STR_LIT>""" <EOL> remind = False <EOL> threshold = int ( self . threshold ) <EOL> for product in card . products : <EOL> if product . remaining_rides is None : <EOL> continue <EOL> try : <EOL> round_trips = product . remaining_rides / <NUM_LIT> <EOL> should_remind = round_trips < threshold <EOL> if last_state : <EOL> last_trips = None <EOL> for last in last_state . products : <EOL> if last . name != product . name or last . remaining_rides is None : <EOL> continue <EOL> last_trips = last . remaining_rides / <NUM_LIT> <EOL> break <EOL> if last_trips is not None : <EOL> pedantic_remind = last_trips != round_trips and should_remind <EOL> app . logger . info ( '<STR_LIT>' % ( round_trips , product . name , pedantic_remind ) ) <EOL> remind = remind or pedantic_remind <EOL> continue <EOL> remind = remind or should_remind <EOL> except : <EOL> app . logger . exception ( '<STR_LIT>' ) <EOL> pass <EOL> return remind <EOL> def _check_ride_reminder ( self , card , last_state = None ) : <EOL> """<STR_LIT>""" <EOL> remind = False <EOL> threshold = int ( self . threshold ) <EOL> for product in card . products : <EOL> if product . remaining_rides is None : <EOL> continue <EOL> try : <EOL> remaining_rides = int ( product . remaining_rides ) <EOL> should_remind = remaining_rides <= threshold <EOL> if last_state : <EOL> last_rides = None <EOL> for last in last_state . products : <EOL> if last . name != product . name or last . remaining_rides is None : <EOL> continue <EOL> last_rides = int ( last . remaining_rides ) <EOL> break <EOL> if last_rides is not None : <EOL> pedantic_remind = last_rides != remaining_rides and should_remind <EOL> app . logger . info ( '<STR_LIT>' % ( remaining_rides , product . name , pedantic_remind ) ) <EOL> remind = remind or pedantic_remind <EOL> continue <EOL> remind = remind or should_remind <EOL> except : <EOL> app . logger . exception ( '<STR_LIT>' ) <EOL> pass <EOL> return remind <EOL> def check_all_cards ( self , force = False ) : <EOL> from breezeminder . models . card import BreezeCard <EOL> for card in BreezeCard . objects . filter ( owner = self . owner ) : <EOL> self . remind ( card , force = force ) <EOL> def remind ( self , card , force = False , last_state = None ) : <EOL> last_reminder_sent = self . last_reminded ( card ) <EOL> if not force and last_reminder_sent and ( datetime . now ( ) - last_reminder_sent . sent_date ) . days < <NUM_LIT:1> : <EOL> app . logger . info ( '<STR_LIT>' % ( self . type , <EOL> card . last_four , <EOL> last_reminder_sent ) ) <EOL> return <EOL> app . logger . info ( '<STR_LIT>' % ( self . type , card . last_four ) ) <EOL> if not self . check_reminder ( card , last_state = last_state ) : <EOL> app . logger . info ( '<STR_LIT>' % ( card . last_four , self . type ) ) <EOL> return <EOL> from flask import render_template <EOL> from breezeminder . app import context , filters <EOL> from breezeminder . models . messaging import Messaging <EOL> app . logger . info ( '<STR_LIT>' % ( self . owner . id , self . type ) ) <EOL> subject = '<STR_LIT>' % self . type . name <EOL> messages = [ ] <EOL> context = { <EOL> '<STR_LIT>' : card , <EOL> '<STR_LIT>' : self , <EOL> '<STR_LIT>' : last_state <EOL> } <EOL> if self . type == '<STR_LIT>' : <EOL> delta = self . _exp_delta ( ) <EOL> expiring = { <EOL> '<STR_LIT>' : self . _will_expire ( card . expiration_date , delta ) <EOL> } <EOL> expiring [ '<STR_LIT>' ] = [ p for p in card . products if self . _will_expire ( p . expiration_date , delta ) ] <EOL> context [ '<STR_LIT>' ] = expiring <EOL> if self . send_email : <EOL> template = '<STR_LIT>' % self . type . key . lower ( ) <EOL> with app . test_request_context ( ) : <EOL> content = render_template ( template , ** context ) <EOL> Messaging . objects . create ( recipients = [ self . owner . email ] , <EOL> sender = app . config [ '<STR_LIT>' ] , <EOL> subject = subject , <EOL> message = content ) <EOL> if self . send_sms : <EOL> recipients = [ ] <EOL> if self . owner . can_receive_sms : <EOL> recipients . append ( self . owner . cell_phone . sms_address ) <EOL> if len ( recipients ) > <NUM_LIT:0> : <EOL> template = '<STR_LIT>' % self . type . key . lower ( ) <EOL> with app . test_request_context ( ) : <EOL> content = render_template ( template , ** context ) <EOL> content = re . sub ( '<STR_LIT>' , '<STR_LIT:U+0020>' , content ) <EOL> subject = '<STR_LIT>' % self . type . name <EOL> Messaging . objects . create ( recipients = recipients , <EOL> sender = app . config [ '<STR_LIT>' ] , <EOL> subject = subject . upper ( ) , <EOL> message = content , <EOL> is_plain = True ) <EOL> try : <EOL> with app . test_request_context ( ) : <EOL> web_message = render_template ( '<STR_LIT>' % self . type . key . lower ( ) , ** context ) <EOL> ReminderHistory . objects . create ( reminder = self , <EOL> card = card , <EOL> message = web_message , <EOL> sent_date = datetime . now ( ) , <EOL> owner = self . owner ) <EOL> except : <EOL> app . logger . exception ( '<STR_LIT>' ) <EOL> self . save ( ) <EOL> class ReminderHistory ( BaseDocument ) : <EOL> reminder = app . db . ReferenceField ( '<STR_LIT>' , required = True , dbref = False , <EOL> reverse_delete_rule = app . db . CASCADE ) <EOL> card = app . db . ReferenceField ( '<STR_LIT>' , required = True , dbref = False , <EOL> reverse_delete_rule = app . db . CASCADE ) <EOL> owner = app . db . ReferenceField ( '<STR_LIT>' , required = True , dbref = False , <EOL> reverse_delete_rule = app . db . CASCADE ) <EOL> sent_date = app . db . DateTimeField ( required = True ) <EOL> message = app . db . StringField ( ) <EOL> meta = { <EOL> '<STR_LIT>' : '<STR_LIT>' , <EOL> '<STR_LIT>' : ReminderHistoryQuerySet , <EOL> '<STR_LIT>' : [ '<STR_LIT>' ] , <EOL> '<STR_LIT>' : [ '<STR_LIT>' ] <EOL> } </s>
