def post(self): #---------------------------------------------------------------------- # request.form is werkzeug.datastructures.ImmutableMultiDict # and each field will show up as list if we don't convert to dict here form = {k:v for k,v in list(request.form.items())} # process a couple of form entries for easier reading theseservices = [] for service in ['is_finish_line', 'is_course_marking', 'is_premium_promotion']: if form.get(service, False): # skip 'is_' portion theseservices.append(service[3:]) services = ', '.join(theseservices) form['services'] = services form['is_new_race'] = 'yes' if form.get('is_new_race', False) else 'no' # turn form into email html = render_template( 'events-servicesquery-mail.jinja2', **form) # TODO: this should be in database subject = '[FSRC-RACE-REQUEST] {}'.format(form['subject']) tolist = current_app.config['SERVICEQUERY_TO'] # TODO: these should be in database cclist = current_app.config['SERVICEQUERY_CC'] + ['{} <{}>'.format(form['contact_name'], form['contact_email'])] fromlist = current_app.config['SERVICEQUERY_CONTACT'] sendmail( subject, fromlist, tolist, html, ccaddr=cclist ) context = { 'pagename' : 'request race services - success', 'pageassets_css' : 'materialize-css', 'pageassets_js' : 'materialize-js', 'servicesquery_contact' : current_app.config['SERVICEQUERY_CONTACT'], } return render_template( 'events-servicesquery-success.jinja2', **context )
def post(self): #---------------------------------------------------------------------- try: # request.form is werkzeug.datastructures.ImmutableMultiDict # and each field will show up as list if we don't convert to dict here # form = {k:v for k,v in request.form.items()} form = loads(request.form['json']) # figure out RD for this race race = SponsorRace.query.filter_by(race=form['race']['text']).one() rdemail = race.email # log this request, won't be committed until after sendmail log = SponsorQueryLog(**{k:form[k]['text'] for k in form['_keyorder']}) log.time = dt.dt2asc(datetime.now()) db.session.add(log) # turn form into email html = render_template( 'sponsorship-email.jinja2', formdata=form) subject = 'Thanks for sponsoring {}!'.format(race.race) # TODO: these could be in database tolist = '{} <{}>'.format(form['name']['text'], form['email']['text']) cclist = [rdemail] + current_app.config['SPONSORSHIPQUERY_CC'] fromlist = '{} <{}>'.format(race.race, current_app.config['SPONSORSHIPQUERY_CONTACT']) sendmail( subject, fromlist, tolist, html, ccaddr=cclist ) # everything seemed to work ok, so committing db.session.commit() return 'OK' except: return "<br/>{}".format(format_exc())
def editor_method_posthook(self, form): #---------------------------------------------------------------------- ''' send contract to client contact if asked to do so, after processing put() note row has already been committed to the database, so can be retrieved ''' # the following can be true only for put() [edit] method if 'addlaction' in form and form['addlaction'] in [ 'sendcontract', 'resendcontract' ]: folderid = current_app.config['CONTRACTS_DB_FOLDER'] # need an instance of contract manager to take care of saving the contract cm = ContractManager(contractType='race services', templateType='contract', driveFolderId=folderid) # pull record(s) from database and save as flat dotted record data = get_request_data(form) print(('data={}'.format(data))) for thisid in data: eventdb = Event.query.filter_by(id=thisid).one() # different subject line if contract had been accepted before. This must match contractviews.AcceptAgreement.post annotation = '' # if we are generating a new version of the contract if form['addlaction'] == 'sendcontract': # if there was already a document sent, indicate that we're updating it if eventdb.contractDocId: eventdb.isContractUpdated = True annotation = '(updated) ' # check appropriate fields are present for certain services servicenames = {s.service for s in eventdb.services} if servicenames & {'coursemarking', 'finishline'}: self._fielderrors = [] for field in [ 'race', 'date', 'mainStartTime', 'mainDistance' ]: if not data[thisid][field]: self._fielderrors.append({ 'name': field, 'status': 'please supply' }) ## handle select fields for field in [ 'state', 'services', 'client', 'course', 'lead' ]: if not data[thisid][field]['id']: self._fielderrors.append({ 'name': '{}.id'.format(field), 'status': 'please select' }) if self._fielderrors: raise parameterError('missing fields') # calculate service fees servicefees = [] feetotal = 0 for service in eventdb.services: servicefee = {'service': service.serviceLong} # fixed fee if service.feeType.feeType == 'fixed': thisfee = service.fee servicefee['fee'] = thisfee servicefees.append(servicefee) # fee is based on another field elif service.feeType.feeType == 'basedOnField': field = service.basedOnField # not clear why this needs to be converted to int, but otherwise see unicode value # if can't be converted, then invalid format try: fieldval = int(getattr(eventdb, field)) except (TypeError, ValueError) as e: fieldval = None # field not set, then set self._fielderrors appropriately if not fieldval: formfield = self.dbmapping[ field] # hopefully not a function self._fielderrors = [{ 'name': formfield, 'status': 'needed to calculate fee' }] raise parameterError( 'cannot calculate fee if {} not set'. format(field)) feebasedons = FeeBasedOn.query.filter_by( serviceId=service.id).order_by( FeeBasedOn.fieldValue).all() foundfee = False for feebasedon in feebasedons: lastfieldval = feebasedon.fieldValue if debug: current_app.logger.debug( 'fieldval={} feebasedon.fieldValue={}'. format(fieldval, feebasedon.fieldValue)) if debug: current_app.logger.debug( 'type(fieldval)={} type(feebasedon.fieldValue)={}' .format(type(fieldval), type(feebasedon.fieldValue))) if fieldval <= feebasedon.fieldValue: thisfee = feebasedon.fee servicefee['fee'] = thisfee servicefees.append(servicefee) foundfee = True break # if fee not found, then set fielderrors appropriately if not foundfee: formfield = self.dbmapping[ field] # hopefully not a function self._fielderrors = [{ 'name': formfield, 'status': 'cannot calculate fee if this is greater than {}' .format(lastfieldval) }] raise parameterError( 'cannot calculate fee if {} greater than {}' .format(field, lastfieldval)) # not sure how we could get here, but best to be defensive else: raise parameterError('unknown feeType: {}'.format( service.feeType.feeType)) # accumulate total fee feetotal += thisfee # need to calculate addons in addition to services for addon in eventdb.addOns: thisfee = addon.fee servicefee = { 'service': addon.longDescr, 'fee': thisfee } servicefees.append(servicefee) # accumulate total fee feetotal += thisfee # generate contract if debug: current_app.logger.debug( 'editor_method_posthook(): (before create()) eventdb.__dict__={}' .format(eventdb.__dict__)) docid = cm.create( '{}-{}-{}.docx'.format(eventdb.client.client, eventdb.race.race, eventdb.date), eventdb, addlfields={ 'servicenames': [s.service for s in eventdb.services], 'servicefees': servicefees, 'event': eventdb.race.race, 'totalfees': { 'service': 'TOTAL', 'fee': feetotal }, }) # update database to show contract sent eventdb.state = State.query.filter_by( state=STATE_CONTRACT_SENT).one() eventdb.contractSentDate = dt.dt2asc(date.today()) eventdb.contractDocId = docid # find index with correct id and show database updates for resprow in self._responsedata: if resprow['rowid'] == thisid: resprow['state'] = { key: val for (key, val ) in list(eventdb.state.__dict__.items()) if key[0] != '_' } resprow[ 'contractSentDate'] = eventdb.contractSentDate resprow['contractDocId'] = eventdb.contractDocId # if we are just resending current version of the contract else: docid = eventdb.contractDocId annotation = '(resend) ' # email sent depends on current state as this flows from 'sendcontract' and 'resendcontract' if eventdb.state.state == STATE_COMMITTED: # prepare agreement accepted email templatestr = (db.session.query(Contract).filter( Contract.contractTypeId == ContractType.id).filter( ContractType.contractType == 'race services' ).filter( Contract.templateTypeId == TemplateType.id).filter( TemplateType.templateType == 'agreement accepted view').one()).block template = Template(templatestr) subject = '{}ACCEPTED - FSRC Race Support Agreement: {} - {}'.format( annotation, eventdb.race.race, eventdb.date) elif eventdb.state.state == STATE_CONTRACT_SENT: # send contract mail to client templatestr = (db.session.query(Contract).filter( Contract.contractTypeId == ContractType.id).filter( ContractType.contractType == 'race services' ).filter( Contract.templateTypeId == TemplateType.id).filter( TemplateType.templateType == 'contract email').one()).block template = Template(templatestr) subject = '{}FSRC Race Support Agreement: {} - {}'.format( annotation, eventdb.race.race, eventdb.date) # state must be STATE_COMMITTED or STATE_CONTRACT_SENT, else logic error else: raise parameterError( 'editor_method_posthook(): bad state seen for {}: {}'. format(form['addlaction'], eventdb.state.state)) # merge database fields into template and send email mergefields = deepcopy(eventdb.__dict__) mergefields[ 'viewcontracturl'] = 'https://docs.google.com/document/d/{}/view'.format( docid) mergefields[ 'downloadcontracturl'] = 'https://docs.google.com/document/d/{}/export?format=pdf'.format( docid) # need to bring in full path for email, so use url_root mergefields[ 'acceptcontracturl'] = request.url_root[:-1] + url_for( 'frontend.acceptagreement', docid=docid) mergefields['servicenames'] = [ s.service for s in eventdb.services ] mergefields['event'] = eventdb.race.race html = template.render(mergefields) tolist = eventdb.client.contactEmail cclist = current_app.config['CONTRACTS_CC'] fromlist = current_app.config['CONTRACTS_CONTACT'] sendmail(subject, fromlist, tolist, html, ccaddr=cclist)
def post(self, docid): #---------------------------------------------------------------------- # this should work because we just did get using same docid thisevent = Event.query.filter_by(contractDocId=docid).one() # get form fields and add to database name = request.form['name'] email = request.form['email'] notes = request.form['notes'] thisevent.contractSignedDate = dt.dt2asc(date.today()) thisevent.contractApprover = name thisevent.contractApproverEmail = email thisevent.contractApproverNotes = notes thisevent.state = State.query.filter_by(state=STATE_COMMITTED).one() # need to get merge fields before commit, first force load of client clientgarbage = thisevent.client mergefields = deepcopy(thisevent.__dict__) db.session.commit() # prepare agreement accepted email and view templatestr = (db.session.query(Contract).filter( Contract.contractTypeId == ContractType.id).filter( ContractType.contractType == 'race services').filter( Contract.templateTypeId == TemplateType.id).filter( TemplateType.templateType == 'agreement accepted view').one()).block # add needed fields mergefields['servicenames'] = [s.service for s in thisevent.services] mergefields['event'] = thisevent.race.race # drive urls # see https://www.labnol.org/internet/direct-links-for-google-drive/28356/ webviewurl = 'https://docs.google.com/document/d/{}/view'.format(docid) mergefields['viewcontracturl'] = webviewurl pdfurl = 'https://docs.google.com/document/d/{}/export?format=pdf'.format( docid) mergefields['downloadcontracturl'] = pdfurl # send agreement accepted email template = Template(templatestr) html = template.render(mergefields) tolist = mergefields['client'].contactEmail cclist = current_app.config['CONTRACTS_TREAS_CC'] fromlist = current_app.config['CONTRACTS_CONTACT'] print(('mergefields={}'.format(mergefields))) # different subject line if contract had been accepted before. This must match eventscontract.EventsContract.editor_method_posthook annotation = '' if thisevent.isContractUpdated: annotation = '(updated) ' subject = '{}ACCEPTED - FSRC Race Support Agreement: {} - {}'.format( annotation, mergefields['event'], mergefields['date']) sendmail(subject, fromlist, tolist, html, ccaddr=cclist) # update for web view mergefields['webview'] = True if debug: current_app.logger.debug( 'AcceptAgreement.post(): mergefields={}'.format(mergefields)) return render_template_string(templatestr, **mergefields)
def editor_method_posthook(self, form): #---------------------------------------------------------------------- ''' send contract to client contact if asked to do so, after processing put() note row has already been committed to the database, so can be retrieved ''' # someday we might allow multiple records to be processed in a single request # pull record(s) from database and save as flat dotted record data = get_request_data(form) for thisid in data: sponsordb = Sponsor.query.filter_by(id=thisid).one_or_none() # if we're creating, we just flushed the row, but the id in the form was 0 # retrieve the created row through saved id if not sponsordb: thisid = self.created_id sponsordb = Sponsor.query.filter_by(id=thisid).one() # the following can be true only for put() [edit] method if 'addlaction' in form and form['addlaction'] in [ 'sendcontract', 'resendcontract' ]: folderid = current_app.config['CONTRACTS_DB_FOLDER'] # need an instance of contract manager to take care of saving the contract cm = ContractManager( contractType='race sponsorship', templateType='sponsor agreement', driveFolderId=folderid, doctype='html', ) racedate = SponsorRaceDate.query.filter_by( race_id=sponsordb.race.id, raceyear=sponsordb.raceyear).one() # bring in subrecords garbage = sponsordb.race garbage = sponsordb.client garbage = sponsordb.level # calculate the benefits (see https://stackoverflow.com/questions/40699642/how-to-query-many-to-many-sqlalchemy) benefitsdb = SponsorBenefit.query.join( SponsorBenefit.levels).filter( SponsorLevel.id == sponsordb.level.id).order_by( SponsorBenefit.order).all() benefits = [b.benefit for b in benefitsdb] # calculate display for coupon count. word (num) if less than 10, otherwise num # but note there may not be a coupon count # ccouponcount is capitalized ncoupons = sponsordb.level.couponcount if ncoupons: if ncoupons < 10: wcoupons = 'zero one two three four five six seven eight nine'.split( )[ncoupons] couponcount = '{} ({})'.format( wcoupons, ncoupons) if ncoupons else None else: couponcount = str(ncoupons) ccouponcount = couponcount.capitalize() else: couponcount = None ccouponcount = None # pick up variables variablesdb = SponsorRaceVbl.query.filter_by( race_id=sponsordb.race.id).all() variables = {v.variable: v.value for v in variablesdb} # if we are generating a new version of the contract if form['addlaction'] == 'sendcontract': # set up dateagreed, if not already there if not sponsordb.dateagreed: sponsordb.dateagreed = dt.dt2asc(date.today()) # additional fields for contract addlfields = { '_date_': humandt.dt2asc(dt.asc2dt(sponsordb.dateagreed)), '_racedate_': humandt.dt2asc(dt.asc2dt(racedate.racedate)), '_rdcertlogo_': pathjoin(current_app.static_folder, 'rd-cert-logo.png'), '_raceheader_': '<img src="{}" width=6in>'.format( pathjoin( current_app.static_folder, '{}-header.png'.format( sponsordb.race.raceshort.lower()))), '_benefits_': benefits, '_raceloc_': racedate.raceloc, '_racebeneficiary_': racedate.beneficiary, '_couponcount_': ccouponcount, # ok to assume this is first word in sentence } addlfields.update(variables) # generate contract if debug: current_app.logger.debug( 'editor_method_posthook(): (before create()) sponsordb.__dict__={}' .format(sponsordb.__dict__)) docid = cm.create( '{} {} {} Sponsor Agreement'.format( sponsordb.raceyear, sponsordb.race.raceshort, sponsordb.client.client), sponsordb, addlfields=addlfields, ) # update database to show contract sent/agreed sponsordb.state = State.query.filter_by( state=STATE_COMMITTED).one() sponsordb.contractDocId = docid # find index with correct id and show database updates for resprow in self._responsedata: if resprow['rowid'] == thisid: resprow['state'] = { key: val for (key, val) in list( sponsordb.state.__dict__.items()) if key[0] != '_' } resprow['dateagreed'] = sponsordb.dateagreed resprow['contractDocId'] = sponsordb.contractDocId # configure coupon provider with coupon code (supported providers) if sponsordb.race.couponprovider and sponsordb.level.couponcount and sponsordb.level.couponcount > 0: expiration = racedate.racedate numregistrations = sponsordb.level.couponcount clientname = sponsordb.client.client raceid = sponsordb.race.couponproviderid couponcode = sponsordb.couponcode start = sponsordb.dateagreed if sponsordb.race.couponprovider.lower( ) == 'runsignup': with RunSignUp( key=current_app.config['RSU_KEY'], secret=current_app.config['RSU_SECRET'], debug=debug) as rsu: coupons = rsu.getcoupons(raceid, couponcode) # rsu search includes any coupons with the couponcode with the coupon string, so we need to filter coupons = [ c for c in coupons if c['coupon_code'] == couponcode ] if coupons: coupon = coupons[ -1] # should be only one entry, but last is the current one (?) coupon_id = coupon['coupon_id'] # override start with the date portion of start_date start = coupon['start_date'].split(' ')[0] else: coupon_id = None rsu.setcoupon(raceid, couponcode, start, expiration, numregistrations, clientname, coupon_id=coupon_id) # if we are just resending current version of the contract else: docid = sponsordb.contractDocId # prepare agreement email (new contract or resending) templatestr = (db.session.query(Contract).filter( Contract.contractTypeId == ContractType.id).filter( ContractType.contractType == 'race sponsorship').filter( Contract.templateTypeId == TemplateType.id).filter( TemplateType.templateType == 'sponsor email').one()).block template = Template(templatestr) subject = '{} Sponsorship Agreement for {}'.format( sponsordb.race.race, sponsordb.client.client) # bring in subrecords garbage = sponsordb.race # merge database fields into template and send email mergefields = deepcopy(sponsordb.__dict__) mergefields[ 'viewcontracturl'] = 'https://docs.google.com/document/d/{}/view'.format( docid) mergefields[ 'downloadcontracturl'] = 'https://docs.google.com/document/d/{}/export?format=pdf'.format( docid) # need to bring in full path for email, so use url_root # mergefields['_race_'] = sponsordb.race.race racedate = SponsorRaceDate.query.filter_by( race_id=sponsordb.race.id, raceyear=sponsordb.raceyear).one() mergefields['_racedate_'] = humandt.dt2asc( dt.asc2dt(racedate.racedate)) mergefields['_coupondate_'] = variables['_coupondate_'] mergefields['_couponcount_'] = couponcount html = template.render(mergefields) tolist = sponsordb.client.contactEmail rdemail = '{} <{}>'.format(sponsordb.race.racedirector, sponsordb.race.rdemail) cclist = current_app.config['SPONSORSHIPAGREEMENT_CC'] + [ rdemail ] fromlist = '{} <{}>'.format( sponsordb.race.race, current_app.config['SPONSORSHIPQUERY_CONTACT']) sendmail(subject, fromlist, tolist, html, ccaddr=cclist) # calculate and update trend calculateTrend(sponsordb) # kludge to force response data to have correct trend # TODO: remove when #245 fixed thisndx = [i['rowid'] for i in self._responsedata].index(thisid) self._responsedata[thisndx]['trend'] = sponsordb.trend
def preraceemail(startdate, enddate): #---------------------------------------------------------------------- '''Send pre-race email to race director and lead.''' # set up tag which is used to control this email senttag = Tag.query.filter_by(tag=TAG_PRERACEMAILSENT).one() inhibittag = Tag.query.filter_by(tag=TAG_PRERACEMAILINHIBITED).one() # calculate start and end date window if startdate == 'auto' and enddate == 'auto': # calculate start and end date window start = dbdate.dt2asc(date.today()) end = dbdate.dt2asc(date.today() + timedelta(app.config['DAYS_PRERACE_EMAIL'])) # verify both dates are present, check user input format is yyyy-mm-dd else: if startdate == 'auto' or enddate == 'auto': print('ERROR: startdate and enddate must both be specified') return if (not match(r'^(19[0-9]{2}|2[0-9]{3})-(0[1-9]|1[012])-([123]0|[012][1-9]|31)$', startdate) or not match(r'^(19[0-9]{2}|2[0-9]{3})-(0[1-9]|1[012])-([123]0|[012][1-9]|31)$', enddate)): print('ERROR: startdate and enddate must be in yyyy-mm-dd format') return # cli specified dates format is fine, and both dates specified start = startdate end = enddate # use correct filter to get races in next N days events = Event.query.filter(Event.date.between(start, end)).all() for event in events: # ignore uncommitted events if event.state.state != STATE_COMMITTED: continue # don't send if this message has already been sent or was inhibited by admin if senttag in event.tags or inhibittag in event.tags: continue # don't send if only premium promotion service if len(event.services) == 1 and event.services[0].service == 'premiumpromotion': continue # send pre-race mail to client templatestr = (db.session.query(Contract) .filter(Contract.contractTypeId==ContractType.id) .filter(ContractType.contractType=='race services') .filter(Contract.templateTypeId==TemplateType.id) .filter(TemplateType.templateType=='pre-race email') .one() ).block template = Template( templatestr ) # bring in needed relations garbage = event.client garbage = event.lead garbage = event.course # merge database fields into template and send email mergefields = deepcopy(event.__dict__) docid = event.contractDocId mergefields['viewcontracturl'] = 'https://docs.google.com/document/d/{}/view'.format(docid) mergefields['servicenames'] = [s.service for s in event.services] mergefields['event'] = event.race.race html = template.render( mergefields ) subject = 'FSRC Pre-race Coordination: {} - {}'.format(event.race.race, event.date) tolist = event.client.contactEmail cclist = app.config['CONTRACTS_CC'] + [event.lead.email] fromlist = app.config['CONTRACTS_CONTACT'] sendmail( subject, fromlist, tolist, html, ccaddr=cclist ) # mark as sent event.tags.append(senttag) db.session.commit()
def sendrenewemails(startdate, enddate): #---------------------------------------------------------------------- '''(initial deployment) Send "renewal" emails for renewed events between two dates yyyy-mm-dd. NOTE: emails are not sent to premium promotion only events. ''' # set up tag which is used to control this email senttag = Tag.query.filter_by(tag=TAG_POSTRACEMAILSENT).one() inhibittag = Tag.query.filter_by(tag=TAG_POSTRACEMAILINHIBITED).one() # check input argument format if (not match(r'^(19[0-9]{2}|2[0-9]{3})-(0[1-9]|1[012])-([123]0|[012][1-9]|31)$', startdate) or not match(r'^(19[0-9]{2}|2[0-9]{3})-(0[1-9]|1[012])-([123]0|[012][1-9]|31)$', enddate)): print('ERROR: dates must be in yyyy-mm-dd format') return # do arguments make sense? if enddate < startdate: print('ERROR: enddate must be greater than or equal to startdate') return # use filter to get races specified events = Event.query.filter(Event.date.between(startdate, enddate)).all() for event in events: # ignore events which are not renewed renewed if event.state.state != STATE_RENEWED_PENDING: continue # bring in event this was renewed from # the latest event associated with this race is the one, but before this one prevevents = Event.query.filter(Event.race_id == event.race_id).filter(Event.date < event.date).order_by(Event.date).all() prevevent = prevevents[-1] # don't send email if this message has already been sent or was inhibited by admin # don't send email if only premium promotion service if senttag in prevevent.tags or inhibittag in prevevent.tags: continue if len(event.services) == 1 and event.services[0].service == 'premiumpromotion': continue # get post-race mail template templatestr = (db.session.query(Contract) .filter(Contract.contractTypeId==ContractType.id) .filter(ContractType.contractType=='race services') .filter(Contract.templateTypeId==TemplateType.id) .filter(TemplateType.templateType=='post-race email') .one() ).block template = Template( templatestr ) # bring in needed relations garbage = event.client garbage = event.lead garbage = event.course # merge database fields into template and send email # deepcopy getting error AttributeError: 'Race' object has no attribute '_sa_instance_state' # so just collect what we need mergefields = deepcopy(event.__dict__) mergefields['nextyeartext'] = 'based on your most recent race' mergefields['servicenames'] = [s.service for s in event.services] mergefields['event'] = event.race.race # this shouldn't happen, but need to handle case where renew_event couldn't find renewed race mergefields['renew_date'] = event.date if event else '[oops - an error occurred determining race date, please contact us]' html = template.render( mergefields ) subject = 'FSRC Race Support Services is holding {} for {}'.format(event.date, event.race.race) tolist = event.client.contactEmail cclist = app.config['CONTRACTS_CC'] fromlist = app.config['CONTRACTS_CONTACT'] sendmail( subject, fromlist, tolist, html, ccaddr=cclist ) # mark as sent prevevent.tags.append(senttag) # pick up all db changes (event.tags) db.session.commit()
def cancellaterace(startdate, enddate): # ---------------------------------------------------------------------- '''Send pre-race premium promotion email.''' # set up tags which are used to control this email senttag = Tag.query.filter_by(tag=TAG_PRERACERENEWEDCANCELED).one() # calculate start and end date window if startdate == 'auto' and enddate == 'auto': # only send for races within one week window # this causes sending DAYS_PRERACE_LATE_CANCEL in advance of the event, # but if it doesn't happen for some reason will retry for a week start = dbdate.dt2asc(date.today() + timedelta(app.config['DAYS_PRERACE_LATE_CANCEL']) - timedelta(7)) end = dbdate.dt2asc(date.today() + timedelta(app.config['DAYS_PRERACE_LATE_CANCEL'])) # verify both dates are present, check user input format is yyyy-mm-dd else: if startdate == 'auto' or enddate == 'auto': print('ERROR: startdate and enddate must both be specified') return if (not match(r'^(19[0-9]{2}|2[0-9]{3})-(0[1-9]|1[012])-([123]0|[012][1-9]|31)$', startdate) or not match(r'^(19[0-9]{2}|2[0-9]{3})-(0[1-9]|1[012])-([123]0|[012][1-9]|31)$', enddate)): print('ERROR: startdate and enddate must be in yyyy-mm-dd format') return # cli specified dates format is fine, and both dates specified start = startdate end = enddate # debug if debug: print('start={} end={}'.format(start, end)) # use filter to get races in which occurred at least N days ago events = Event.query.filter(Event.date.between(start, end)).all() for event in events: # ignore events unless they're in renewed-pending state if event.state.state != STATE_RENEWED_PENDING: continue # don't send email if this message has already been sent or was inhibited by admin # this is done for all events, regardless of what services were 'renewed' if senttag in event.tags: continue # cancel event event.state = State.query.filter_by(state=STATE_CANCELED).one() # get canceled email template templatestr = (db.session.query(Contract) .filter(Contract.contractTypeId == ContractType.id) .filter(ContractType.contractType == 'race services') .filter(Contract.templateTypeId == TemplateType.id) .filter(TemplateType.templateType == 'canceled email') .one() ).block template = Template(templatestr) # bring in needed relations garbage = event.client # merge database fields into template and send email # deepcopy getting error AttributeError: 'Race' object has no attribute '_sa_instance_state' # so just collect what we need mergefields = deepcopy(event.__dict__) mergefields['event'] = event.race.race mergefields['renew_date'] = event.date html = template.render(mergefields) subject = 'Automatically canceling {}'.format(event.race.race) tolist = app.config['CONTRACTS_CC'] cclist = None fromlist = app.config['CONTRACTS_CONTACT'] sendmail(subject, fromlist, tolist, html, ccaddr=cclist) # mark as sent event.tags.append(senttag) # pick up all db changes (event.tags) db.session.commit()
def postraceprocessing(startdate, enddate): #---------------------------------------------------------------------- '''Sending post-race email and renew race.''' # set up tag which is used to control this email senttag = Tag.query.filter_by(tag=TAG_POSTRACEMAILSENT).one() inhibittag = Tag.query.filter_by(tag=TAG_POSTRACEMAILINHIBITED).one() if startdate == 'auto' and enddate == 'auto': # processing for races DAYS_POSTRACE_EMAIL days after the event, # but if it doesn't happen for some reason will retry for a week # calculate start and end date window (try to send for 1 week) start = dbdate.dt2asc(date.today() - timedelta(app.config['DAYS_POSTRACE_EMAIL'] + 7)) end = dbdate.dt2asc(date.today() - timedelta(app.config['DAYS_POSTRACE_EMAIL'])) # verify both dates are present, check user input format is yyyy-mm-dd else: if startdate == 'auto' or enddate == 'auto': print('ERROR: startdate and enddate must both be specified') return if (not match(r'^(19[0-9]{2}|2[0-9]{3})-(0[1-9]|1[012])-([123]0|[012][1-9]|31)$', startdate) or not match(r'^(19[0-9]{2}|2[0-9]{3})-(0[1-9]|1[012])-([123]0|[012][1-9]|31)$', enddate)): print('ERROR: startdate and enddate must be in yyyy-mm-dd format') return # cli specified dates format is fine, and both dates specified start = startdate end = enddate # use filter to get races in which occurred at least N days ago events = Event.query.filter(Event.date.between(start, end)).all() for event in events: # ignore uncommitted events if event.state.state != STATE_COMMITTED: continue # renew event newevent = renew_event(event) # pick up any db changes related to renewal (renew, daterule, event.tags) db.session.commit() # don't send email if this message has already been sent or was inhibited by admin # don't send email if only premium promotion service if senttag in event.tags or inhibittag in event.tags: continue if len(event.services) == 1 and event.services[0].service == 'premiumpromotion': continue # get post-race mail template templatestr = (db.session.query(Contract) .filter(Contract.contractTypeId==ContractType.id) .filter(ContractType.contractType=='race services') .filter(Contract.templateTypeId==TemplateType.id) .filter(TemplateType.templateType=='post-race email') .one() ).block template = Template( templatestr ) # bring in needed relations garbage = event.client garbage = event.lead garbage = event.course # merge database fields into template and send email # deepcopy getting error AttributeError: 'Race' object has no attribute '_sa_instance_state' # so just collect what we need mergefields = deepcopy(event.__dict__) mergefields['nextyeartext'] = 'for next year' mergefields['servicenames'] = [s.service for s in event.services] mergefields['event'] = event.race.race # this shouldn't happen, but need to handle case where renew_event couldn't find renewed race mergefields['renew_date'] = newevent.date if newevent else '[oops - an error occurred determining race date, please contact us]' surveyfields = { 'eventencoded' : quote_plus(event.race.race), 'dateencoded' : quote_plus(event.date), } surveytemplate = Template( app.config['CONTRACTS_SURVEY_URL'] ) mergefields['surveylink'] = surveytemplate.render( surveyfields ) html = template.render( mergefields ) subject = 'Thank You for using FSRC Race Support Services - {}'.format(event.date) tolist = event.client.contactEmail cclist = app.config['CONTRACTS_CC'] fromlist = app.config['CONTRACTS_CONTACT'] sendmail( subject, fromlist, tolist, html, ccaddr=cclist ) # mark as sent event.tags.append(senttag) # pick up all db changes (event.tags) db.session.commit()
def leademail(startdate, enddate): #---------------------------------------------------------------------- '''Send pre-race email to lead.''' # set up tag which is used to control this email senttag = Tag.query.filter_by(tag=TAG_LEADEMAILSENT).one() # calculate start and end date window if startdate == 'auto' and enddate == 'auto': # only send for races coming up within DAYS_LEAD_EMAIL in advance of the event, # but if it doesn't happen for some reason will retry until event passed start = dbdate.dt2asc(date.today()) end = dbdate.dt2asc(date.today() + timedelta(app.config['DAYS_LEAD_EMAIL'])) # verify both dates are present, check user input format is yyyy-mm-dd else: if startdate == 'auto' or enddate == 'auto': print('ERROR: startdate and enddate must both be specified') return if (not match(r'^(19[0-9]{2}|2[0-9]{3})-(0[1-9]|1[012])-([123]0|[012][1-9]|31)$', startdate) or not match(r'^(19[0-9]{2}|2[0-9]{3})-(0[1-9]|1[012])-([123]0|[012][1-9]|31)$', enddate)): print('ERROR: startdate and enddate must be in yyyy-mm-dd format') return # cli specified dates format is fine, and both dates specified start = startdate end = enddate # use correct filter to get races in next N days events = Event.query.filter(Event.date.between(start, end)).all() for event in events: # ignore uncommitted events if event.state.state != STATE_COMMITTED: continue # don't send if this message has already been sent if senttag in event.tags: continue # don't send if only premium promotion service if len(event.services) == 1 and event.services[0].service == 'premiumpromotion': continue # send pre-race mail to client templatestr = (db.session.query(Contract) .filter(Contract.contractTypeId==ContractType.id) .filter(ContractType.contractType=='race services') .filter(Contract.templateTypeId==TemplateType.id) .filter(TemplateType.templateType=='lead email') .one() ).block template = Template( templatestr ) # bring in needed relations garbage = event.client garbage = event.lead garbage = event.course # merge database fields into template and send email mergefields = deepcopy(event.__dict__) mergefields['servicedescrs'] = [s.serviceLong for s in event.services if s.service != 'premiumpromotion'] mergefields['addlservices'] = [a.longDescr for a in event.addOns] mergefields['event'] = event.race.race html = template.render( mergefields ) subject = 'FSRC race reminders for lead: {} - {}'.format(event.race.race, event.date) tolist = event.lead.email cclist = app.config['CONTRACTS_CC'] fromlist = app.config['CONTRACTS_CONTACT'] sendmail( subject, fromlist, tolist, html, ccaddr=cclist ) # mark as sent event.tags.append(senttag) db.session.commit()