def main(): scriptdir = dirname(__file__) # two levels up scriptfolder = dirname(dirname(scriptdir)) configdir = join(scriptfolder, 'config') memberconfigfile = "members.cfg" memberconfigpath = join(configdir, memberconfigfile) userconfigfile = "users.cfg" userconfigpath = join(configdir, userconfigfile) # create app and get configuration # use this order so members.cfg overrrides users.cfg configfiles = [userconfigpath, memberconfigpath] app = create_app(Development(configfiles), configfiles) # set up database db.init_app(app) # set up scoped session with app.app_context(): # turn on logging setlogging() # assumes MeetingType table was created manually, with Board Meeting as a meeting type motionvotes = MotionVote.query.filter_by().all() for motionvote in motionvotes: if not motionvote.motionvotekey: motionvote.motionvotekey = uuid4().hex db.session.commit()
def main(): scriptdir = dirname(__file__) # two levels up scriptfolder = dirname(dirname(scriptdir)) configdir = join(scriptfolder, 'config') memberconfigfile = "members.cfg" memberconfigpath = join(configdir, memberconfigfile) userconfigfile = "users.cfg" userconfigpath = join(configdir, userconfigfile) # create app and get configuration # use this order so members.cfg overrrides users.cfg configfiles = [userconfigpath, memberconfigpath] app = create_app(Development(configfiles), configfiles) # set up database db.init_app(app) # set up scoped session with app.app_context(): # turn on logging setlogging() fsrcinterest = Interest.query.filter_by(interest='fsrc').one() localfsrcinterest = LocalInterest.query.filter_by( interest_id=fsrcinterest.id).one() # assumes MeetingType table was created manually, with Board Meeting as a meeting type boardmeeting = MeetingType.query.filter_by( meetingtype='Board Meeting', interest=localfsrcinterest).one() meetings = Meeting.query.filter_by(interest=localfsrcinterest).all() for meeting in meetings: meeting.meetingtype = boardmeeting db.session.commit()
def main(): scriptdir = dirname(__file__) # two levels up scriptfolder = dirname(dirname(scriptdir)) configdir = join(scriptfolder, 'config') memberconfigfile = "members.cfg" memberconfigpath = join(configdir, memberconfigfile) userconfigfile = "users.cfg" userconfigpath = join(configdir, userconfigfile) # create app and get configuration # use this order so members.cfg overrrides users.cfg configfiles = [userconfigpath, memberconfigpath] app = create_app(Development(configfiles), configfiles) # set up database db.init_app(app) # set up scoped session with app.app_context(): # turn on logging setlogging() fsrcinterest = Interest.query.filter_by(interest='fsrc').one() localfsrcinterest = LocalInterest.query.filter_by( interest_id=fsrcinterest.id).one() # assumes tag 'board status report' was created manually, crash if not srtag = Tag.query.filter_by(interest=localfsrcinterest, tag='board status report').one() boardmeetingtype = MeetingType.query.filter_by( interest=localfsrcinterest, meetingtype='Board Meeting').one() positions = Position.query.filter_by(interest=localfsrcinterest, has_status_report=True).all() meetings = Meeting.query.filter_by(interest=localfsrcinterest, meetingtype=boardmeetingtype).all() for position in positions: if srtag not in position.tags: position.tags.append(srtag) for meeting in meetings: if srtag not in meeting.statusreporttags: meeting.statusreporttags.append(srtag) db.session.commit()
def main(): parser = ArgumentParser() parser.add_argument('interest') parser.add_argument('--nomembers', default=False, action='store_const', const=True, help='use --nomembers to skip members') parser.add_argument('--nomanagers', default=False, action='store_const', const=True, help='use --nomanagers to skip managers') args = parser.parse_args() scriptdir = dirname(__file__) # two levels up scriptfolder = dirname(dirname(scriptdir)) configdir = join(scriptfolder, 'config') memberconfigfile = "members.cfg" memberconfigpath = join(configdir, memberconfigfile) userconfigfile = "users.cfg" userconfigpath = join(configdir, userconfigfile) # create app and get configuration # use this order so members.cfg overrrides users.cfg configfiles = [userconfigpath, memberconfigpath] app = create_app(Development(configfiles), configfiles) # set up database db.init_app(app) # set up app and request contexts so that taskdetails works # https://flask.palletsprojects.com/en/1.1.x/testing/ with app.app_context(), app.test_request_context(): # turn on logging setlogging() # g has to be set within app context g.interest = args.interest membertasks = [] taskdetails.open() for row in taskdetails.rows: membertask = taskdetails.dte.get_response_data(row) # add user record localuserid, taskid = taskdetails.getids(row.id) membertask['User'] = localuser2user(localuserid) membertask['Task'] = Task.query.filter_by(id=taskid).one() membertasks.append(membertask) # create member based data structure mem2tasks = {} for membertask in membertasks: mem2tasks.setdefault(membertask['User'].email, {'tasks': []}) mem2tasks[membertask['User'].email]['tasks'].append(membertask) for member in mem2tasks: mem2tasks[member]['tasks'].sort(key=lambda t: t['order']) # default is from interest, may be overridden below, based on emailtemplate configuration fromlist = localinterest().from_email # allows for debugging of each section separately if not args.nomembers: emailtemplate = EmailTemplate.query.filter_by( templatename='member-email', interest=localinterest()).one() template = Template(emailtemplate.template) subject = emailtemplate.subject if emailtemplate.from_email: fromlist = emailtemplate.from_email refurl = url_for('admin.taskchecklist', interest=g.interest, _external=True) for emailaddr in mem2tasks: # only send email if there are tasks overdue or upcoming sendforstatus = [STATUS_EXPIRES_SOON, STATUS_OVERDUE] if len([ t for t in mem2tasks[emailaddr]['tasks'] if t['status'] in sendforstatus ]) > 0: html = template.render(**mem2tasks[emailaddr], statuses=sendforstatus, refurl=refurl) tolist = emailaddr cclist = None sendmail(subject, fromlist, tolist, html, ccaddr=cclist) # allows for debugging of each section separately if not args.nomanagers: # what groups are each member a part of? member2groups = {} for memberlocal in LocalUser.query.filter_by( active=True, interest=localinterest()).all(): memberglobal = localuser2user(memberlocal) member2groups[memberglobal.email] = { 'worker': memberlocal, 'taskgroups': set() } # drill down to get all taskgroups the member is responsible for for position in positions_active(memberlocal, date.today()): get_position_taskgroups( position, member2groups[memberglobal.email]['taskgroups']) for taskgroup in memberlocal.taskgroups: get_taskgroup_taskgroups( taskgroup, member2groups[memberglobal.email]['taskgroups']) # get list of responsible managers responsibility = {} positions = Position.query.filter_by( interest=localinterest()).all() for position in positions: positiontasks = set() positionworkers = set() theseemailgroups = set(position.emailgroups) for workeremail in member2groups: # add worker if the worker's taskgroups intersect with these email groups if theseemailgroups & member2groups[workeremail][ 'taskgroups']: positionworkers |= { member2groups[workeremail]['worker'] } for emailgroup in position.emailgroups: get_taskgroup_tasks(emailgroup, positiontasks) # only set responsibility if this position has management for some groups if position.emailgroups: for manager in members_active(position, date.today()): manageruser = localuser2user(manager) responsibility.setdefault(manageruser.email, { 'tasks': set(), 'workers': set() }) responsibility[ manageruser.email]['tasks'] |= positiontasks responsibility[ manageruser.email]['workers'] |= positionworkers # set up template engine emailtemplate = EmailTemplate.query.filter_by( templatename='leader-email', interest=localinterest()).one() template = Template(emailtemplate.template) subject = emailtemplate.subject if emailtemplate.from_email: fromlist = emailtemplate.from_email refurl = url_for('admin.taskdetails', interest=g.interest, _external=True) # loop through responsible managers, setting up their email for manager in responsibility: manager2members = {'members': []} # need to convert to ids which are given by taskdetails for positionworker in responsibility[manager]['workers']: resptasks = [ taskdetails.setid(positionworker.id, t.id) for t in responsibility[manager]['tasks'] ] positionuser = localuser2user(positionworker) thesetasks = [ t for t in mem2tasks[positionuser.email]['tasks'] if t['rowid'] in resptasks and t['status'] in [STATUS_OVERDUE] ] if thesetasks: manager2members['members'].append({ 'name': positionuser.name, 'tasks': thesetasks }) # only send if something to send if manager2members['members']: html = template.render(**manager2members, refurl=refurl) tolist = manager cclist = None sendmail(subject, fromlist, tolist, html, ccaddr=cclist)
def main(): parser = ArgumentParser() parser.add_argument('--noclear', default=False, action='store_const', const=True, help='use --nomembers to skip members') args = parser.parse_args() scriptdir = dirname(__file__) # two levels up scriptfolder = dirname(dirname(scriptdir)) configdir = join(scriptfolder, 'config') memberconfigfile = "members.cfg" memberconfigpath = join(configdir, memberconfigfile) userconfigfile = "users.cfg" userconfigpath = join(configdir, userconfigfile) # create app and get configuration # use this order so members.cfg overrrides users.cfg configfiles = [userconfigpath, memberconfigpath] app = create_app(Development(configfiles), configfiles) # set up database db.init_app(app) # set up scoped session with app.app_context(): # turn on logging setlogging() # clear and initialize the members database # bind=None per https://flask-sqlalchemy.palletsprojects.com/en/2.x/binds/ if not args.noclear: db.drop_all(bind=None) db.create_all(bind=None) update_local_tables() testuser = User.query.filter_by(email='*****@*****.**').one() fsrcinterest = Interest.query.filter_by(interest='fsrc').one() localfsrcinterest = LocalInterest.query.filter_by(interest_id=fsrcinterest.id).one() localtestuser = LocalUser.query.filter_by(user_id=testuser.id, interest_id=localfsrcinterest.id).one() localfsrcinterest.initial_expiration = date(2020, 4, 1) localfsrcinterest.from_email = "*****@*****.**" taskgroups = [ {'taskgroup': 'Board Meeting Attendee', 'description': 'People who attend board meeting'}, {'taskgroup': 'Nomination Process', 'description': 'Tasks about nominating officers and directors'}, {'taskgroup': 'Executive Officer', 'description': 'President, Vice President, Treasurer, Secretary', 'taskgroups': ['Board Meeting Attendee', 'Nomination Process'], }, {'taskgroup': 'Board of Directors', 'description': 'Board of Directors', 'taskgroups': ['Board Meeting Attendee', 'Nomination Process'], }, {'taskgroup': 'Training', 'description': 'Tasks for training group'}, {'taskgroup': 'Youth Training', 'description': 'Tasks for youth trainers'}, {'taskgroup': 'Signature Race Director', 'description': 'Tasks about signature races', 'taskgroups': ['Board Meeting Attendee'], }, {'taskgroup': 'Low Key Race Director', 'description': 'Tasks about low key races'}, {'taskgroup': 'Memorial Scholarship', 'description': 'Tasks about Memorial Scholarship'}, {'taskgroup': 'President', 'description': 'Tasks just for the president'}, {'taskgroup': 'Finances', 'description': 'Tasks for Treasurer / Finance Committee'}, {'taskgroup': 'Races', 'description': 'Tasks for Races Committee'}, {'taskgroup': 'Bank Account Full Access', 'description': 'Task for those who have full access to bank account'}, {'taskgroup': 'General Committee Leadership', 'description': 'Committee lead tasks', 'taskgroups': ['Board Meeting Attendee'], }, {'taskgroup': 'Technology Committee Leadership', 'description': 'Committee lead tasks', 'taskgroups': ['Board Meeting Attendee', 'General Committee Leadership'], }, {'taskgroup': 'Communications Committee Leadership', 'description': 'Committee lead tasks', 'taskgroups': ['Board Meeting Attendee', 'General Committee Leadership'], }, {'taskgroup': 'Membership Committee Leadership', 'description': 'Committee lead tasks', 'taskgroups': ['Board Meeting Attendee', 'General Committee Leadership'], }, ] for taskgroup in taskgroups: subgroups = taskgroup.pop('taskgroups', []) tgp = TaskGroup(interest=localfsrcinterest, **taskgroup) taskgroup['TaskGroup'] = tgp db.session.add(tgp) # need to flush each one so we find subgroups db.session.flush() tgp = TaskGroup.query.filter_by(taskgroup=taskgroup['taskgroup']).one() for subgroup in subgroups: thissubgroup = TaskGroup.query.filter_by(taskgroup=subgroup).one() tgp.taskgroups.append(thissubgroup) ssuploadfield = gen_fieldname() sscompdatefield = gen_fieldname() # ref https://docs.google.com/document/d/1dQcnj-eqwMA9j9k5UuYMlT_3-pImvQxpyPgUIJX9rvo/edit coifieldtype = ['a-output', 'a-input', 'b-output', 'b-input', 'c-input-output'] coifieldnames = [gen_fieldname() for i in range(5)] coifieldneeds = [NEED_OPTIONAL, NEED_ONE_OF, NEED_OPTIONAL, NEED_ONE_OF, NEED_REQUIRED] coifield = dict(zip(coifieldtype, coifieldnames)) coifieldneed = dict(zip(coifieldtype, coifieldneeds)) coitaskfields = {} taskfields = [ # safe sport { 'taskfield': 'Safe Sport Completion Date', 'fieldname': sscompdatefield, 'inputtype':INPUT_TYPE_DATE, 'priority':1, 'displaylabel': 'Safe Sport Completion Date', 'override_completion': True }, { 'taskfield': 'Safe Sport Upload', 'fieldname': ssuploadfield, 'inputtype': INPUT_TYPE_UPLOAD, 'priority':2, 'displaylabel':'Upload Safe Sport Certificate', 'uploadurl':url_for('admin.fieldupload', interest='fsrc') + '?{}={}'.format(FIELDNAME_ARG, ssuploadfield)}, # conflict of interest { 'taskfield': 'COI Display No Conflict', 'fieldname': coifield['a-output'], 'inputtype': INPUT_TYPE_DISPLAY, 'priority': 1, 'displaylabel': 'If no conflicts', 'displayvalue': 'I am not aware of any relationship or interest or situation involving my family or myself ' 'which might result in, or give the appearance of being, a conflict of interest between such ' 'family member or me on one hand and the Frederick Steeplechasers Running Club on the other. '}, { 'taskfield': 'COI Input No Conflict', 'fieldname': coifield['a-input'], 'inputtype': INPUT_TYPE_TEXT, 'priority': 2, 'displaylabel': 'Your initials' }, { 'taskfield': 'COI Display Conflict', 'fieldname': coifield['b-output'], 'inputtype': INPUT_TYPE_DISPLAY, 'priority': 3, 'displaylabel': 'If conflicts', 'displayvalue': 'The following are relationships, interests, or situations involving me or a member of my ' 'family which I consider might result in or appear to be an actual, apparent or potential ' 'conflict of interest between such family members or myself on one hand and the Frederick ' 'Steeplechasers Running Club on the other. ' '\n' '\n* For-profit corporate directorships, positions, and employment with (list)' '\n* Nonprofit trusteeships of positions: Memberships in the following organizations (list)' '\n* Contracts, business activities, and investments with or in the following organizations (list)' '\n* Other relationships and activities (list)' }, { 'taskfield': 'COI Input Conflict', 'fieldname': coifield['b-input'], 'inputtype': INPUT_TYPE_TEXTAREA, 'priority': 4, 'displaylabel': '' }, { 'taskfield': 'COI Input Occupation', 'fieldname': coifield['c-input-output'], 'inputtype': INPUT_TYPE_TEXT, 'priority': 5, 'displaylabel': 'My primary business or occupation at this time is' }, ] for taskfield in taskfields: thisfield = TaskField(interest=localfsrcinterest, **taskfield) db.session.add(thisfield) coitaskfields[taskfield['fieldname']] = thisfield db.session.flush() tasks = [ {'task': 'Executive Officer Job Description', 'priority':2, 'period': 2, 'period_units': DATE_UNIT_YEARS, 'isoptional':False, 'expirysoon': 2, 'expirysoon_units': DATE_UNIT_WEEKS, 'description':'Review [Executive Officer Job Description](https://docs.google.com/document/d/14yW5nK_mc9jiutPmniMmzDjLncjxlD7xA7tRruO43MY/preview)', 'taskgroups': [ next(tg['TaskGroup'] for tg in taskgroups if tg['taskgroup'] == 'Executive Officer') ], }, {'task': 'Board of Directors Job Description', 'priority':2, 'period': 2, 'period_units': DATE_UNIT_YEARS, 'isoptional':False, 'expirysoon': 2, 'expirysoon_units': DATE_UNIT_WEEKS, 'description':'Review [Board of Directors Job Description](https://docs.google.com/document/d/1rps35T5Z5YHKpI_80ZX7CFKaW7TPhuZYBFckCTSNnho/preview)', 'taskgroups': [ next(tg['TaskGroup'] for tg in taskgroups if tg['taskgroup'] == 'Board of Directors') ], }, {'task': 'Board Orientation and Operation', 'priority': 1, 'period': 2, 'period_units': DATE_UNIT_YEARS, 'isoptional': False, 'expirysoon': 2, 'expirysoon_units': DATE_UNIT_WEEKS, 'description': 'Review [FSRC Board Orientation and Operation](https://docs.google.com/document/d/1mwz3zpjv2kjk_LukT2KTtEslk3pWujyAU50yrEvIc1M/preview)', 'taskgroups': [ next(tg['TaskGroup'] for tg in taskgroups if tg['taskgroup'] == 'Board Meeting Attendee') ], }, {'task': 'Articles of Incorporation', 'priority': 5, 'isoptional': False, 'description': 'Review [Articles of Incorporation](https://drive.google.com/open?id=1iMc-qq2mlXQKsilZfXgQFqHfAET227Vj)', 'taskgroups': [ next(tg['TaskGroup'] for tg in taskgroups if tg['taskgroup'] == 'Board Meeting Attendee') ], }, {'task': 'Constitution / Bylaws', 'priority': 5, 'period': 2, 'period_units': DATE_UNIT_YEARS, 'isoptional': False, 'expirysoon': 2, 'expirysoon_units': DATE_UNIT_WEEKS, 'description': 'Review [FSRC Constitution / Bylaws](https://docs.google.com/document/d/1bxULc_jEuzUUSxfDWYIOtAvpnA0LXq7O8kkcybCBEQ0/preview)', 'taskgroups': [ next(tg['TaskGroup'] for tg in taskgroups if tg['taskgroup'] == 'Board Meeting Attendee') ], }, {'task': 'Conflict of Interest Policy', 'priority': 2, 'period': 1, 'period_units': DATE_UNIT_YEARS, 'isoptional': False, 'expirysoon': 2, 'expirysoon_units': DATE_UNIT_WEEKS, 'description': 'Review [Conflict of Interest Policy](https://docs.google.com/document/d/1dQcnj-eqwMA9j9k5UuYMlT_3-pImvQxpyPgUIJX9rvo/preview)', 'fields': [coifieldneed[ft] + '/' + coifield[ft] for ft in coifieldtype], 'taskgroups': [ next(tg['TaskGroup'] for tg in taskgroups if tg['taskgroup'] == 'Board Meeting Attendee') ], }, {'task': 'Board Meeting Procedures', 'priority': 1, 'period': 2, 'period_units': DATE_UNIT_YEARS, 'isoptional': False, 'expirysoon': 2, 'expirysoon_units': DATE_UNIT_WEEKS, 'description': 'Review [Board Meeting Procedures](https://docs.google.com/document/d/1k1eDwEa641Rdd6fRZcRv4bkMJXfXWtWAoyiyw9jtZc0/preview)', 'taskgroups': [ next(tg['TaskGroup'] for tg in taskgroups if tg['taskgroup'] == 'Board Meeting Attendee') ], }, {'task': 'Code of Conduct Policy', 'priority': 2, 'period': 2, 'period_units': DATE_UNIT_YEARS, 'isoptional': False, 'expirysoon': 2, 'expirysoon_units': DATE_UNIT_WEEKS, 'description': 'Review [Code of Conduct Policy](https://docs.google.com/document/d/11MuwstPD1X_8ivuR4TV6qrpXAfUPHoGNnTqlMVHlFoA/preview)', 'taskgroups': [ next(tg['TaskGroup'] for tg in taskgroups if tg['taskgroup'] == 'Board Meeting Attendee') ], }, {'task': 'Social Media Policy', 'priority': 2, 'period': 2, 'period_units': DATE_UNIT_YEARS, 'isoptional': False, 'expirysoon': 2, 'expirysoon_units': DATE_UNIT_WEEKS, 'description': 'Review [Social Media Policy - Leadership Team](https://docs.google.com/document/d/1_sZYz8SLtepLlAT7V9laD0lGcUckXnslMrm9xlaR0_I/preview)', 'taskgroups': [ next(tg['TaskGroup'] for tg in taskgroups if tg['taskgroup'] == 'Board Meeting Attendee') ], }, {'task': 'Executive Board Nomination Process', 'priority': 5, 'period': 2, 'period_units': DATE_UNIT_YEARS, 'isoptional': False, 'expirysoon': 2, 'expirysoon_units': DATE_UNIT_WEEKS, 'description': 'Review [Executive Board Nomination Process](https://docs.google.com/document/d/110KryUEX_77Fj24QJHk9BEbGXAgcPdEIsba8Izwz-AE/preview)', 'taskgroups': [ next(tg['TaskGroup'] for tg in taskgroups if tg['taskgroup'] == 'Nomination Process') ], }, {'task': 'Safe Sport Policy', 'priority': 3, 'period': 1, 'period_units': DATE_UNIT_YEARS, 'isoptional': False, 'expirysoon': 2, 'expirysoon_units': DATE_UNIT_WEEKS, 'description': 'Review [Safe Sport Policy](https://docs.google.com/document/d/1I9TGaf5FmSZsqIWlguZOXyWsVkAU7Q3kXZ_WijBRkH8/preview)', 'taskgroups': [ next(tg['TaskGroup'] for tg in taskgroups if tg['taskgroup'] == 'Training') ], }, {'task': 'Safe Sport Training', 'priority': 4, 'period': 2, 'period_units': DATE_UNIT_YEARS, 'isoptional': False, 'expirysoon': 2, 'expirysoon_units': DATE_UNIT_WEEKS, 'description': 'Complete Safe Sport Training', 'fields': [ NEED_REQUIRED + '/' + sscompdatefield, NEED_REQUIRED + '/' + ssuploadfield, ], 'taskgroups': [ next(tg['TaskGroup'] for tg in taskgroups if tg['taskgroup'] == 'Training') ], }, {'task': 'Training Programs - Youth Participation Policy', 'priority': 5, 'period': 2, 'period_units': DATE_UNIT_YEARS, 'isoptional': False, 'expirysoon': 2, 'expirysoon_units': DATE_UNIT_WEEKS, 'description': 'Review [Training Programs - Youth Participation Policy](https://docs.google.com/document/d/1jjDoqAkfKxFfTN16uMW2-J1ptW8o7DflocloFjjpm0A/preview)', 'taskgroups': [ next(tg['TaskGroup'] for tg in taskgroups if tg['taskgroup'] == 'Training') ], }, {'task': 'RRCA Coach Certification', 'priority': 6, 'isoptional': True, 'description': 'Take RRCA Coach Certification Course', 'taskgroups': [ next(tg['TaskGroup'] for tg in taskgroups if tg['taskgroup'] == 'Training') ], }, {'task': 'First Aid Training', 'priority': 6, 'isoptional': True, 'description': 'Take First Aid Training (required by RRCA every two years for RRCA Certified Coachesee', 'taskgroups': [ next(tg['TaskGroup'] for tg in taskgroups if tg['taskgroup'] == 'Training') ], }, {'task': 'Background Check', 'priority': 2, 'period': 2, 'period_units': DATE_UNIT_YEARS, 'isoptional': False, 'expirysoon': 2, 'expirysoon_units': DATE_UNIT_WEEKS, 'description': 'Apply for background check. ' 'For instructions see [NCSI Self Registration Letter](https://drive.google.com/file/d/0B8Zxg7taJn4FR2RVc2ZiQ2tmeVhiNlRCT01IUnVoRHRhVWIw/view?usp=sharing)', 'taskgroups': [ next(tg['TaskGroup'] for tg in taskgroups if tg['taskgroup'] == 'Youth Training'), next(tg['TaskGroup'] for tg in taskgroups if tg['taskgroup'] == 'Bank Account Full Access'), ], }, {'task': 'Race Revenue Proposal', 'priority': 3, 'dateofyear': '11-01', 'expirystarts': 9, 'expirystarts_units': DATE_UNIT_MONTHS, 'isoptional': False, 'expirysoon': 4, 'expirysoon_units': DATE_UNIT_WEEKS, 'description': 'Produce Race Revenue Proposal based on ' '[FSRC Event Revenue Policy](https://docs.google.com/document/d/1K-x_xY1b2nWS8qZ2fljauJl_Plbb0qK2ANjy-43cEU8/preview)', 'taskgroups': [ next(tg['TaskGroup'] for tg in taskgroups if tg['taskgroup'] == 'Signature Race Director') ], }, {'task': 'Race Policy', 'priority': 3, 'period': 2, 'period_units': DATE_UNIT_YEARS, 'isoptional': False, 'expirysoon': 2, 'expirysoon_units': DATE_UNIT_WEEKS, 'description': 'Review ' '[FSRC Race Policy](https://docs.google.com/document/d/1UxT1Z9sjcKJyFjgLpfDtkSHQ-lzIam0t5lbxcBAjYGo/preview)', 'taskgroups': [ next(tg['TaskGroup'] for tg in taskgroups if tg['taskgroup'] == 'Signature Race Director'), next(tg['TaskGroup'] for tg in taskgroups if tg['taskgroup'] == 'Low Key Race Director'), ], }, {'task': 'Severe Weather Cancellation Policy', 'priority': 3, 'period': 2, 'period_units': DATE_UNIT_YEARS, 'isoptional': False, 'expirysoon': 2, 'expirysoon_units': DATE_UNIT_WEEKS, 'description': 'Review ' '[Severe Weather Cancellation Policy](https://docs.google.com/document/d/1s14YePWI6chccrTtibyBokyigaG-yAx__jpcni611Bk/preview)', 'taskgroups': [ next(tg['TaskGroup'] for tg in taskgroups if tg['taskgroup'] == 'Signature Race Director'), next(tg['TaskGroup'] for tg in taskgroups if tg['taskgroup'] == 'Low Key Race Director'), ], }, {'task': 'RRCA Race Director Certification', 'priority': 3, 'isoptional': True, 'description': 'Take RRCA Race Director Certification course', 'taskgroups': [ next(tg['TaskGroup'] for tg in taskgroups if tg['taskgroup'] == 'Signature Race Director'), ], }, {'task': 'Memorial Scholarship Policy', 'priority': 3, 'period': 2, 'period_units': DATE_UNIT_YEARS, 'isoptional': False, 'expirysoon': 2, 'expirysoon_units': DATE_UNIT_WEEKS, 'description': 'Review ' '[FSRC Memorial Scholarship Policy](https://docs.google.com/document/d/1aF-m32Y6x3Nl0AQZWCgsnv2cw25JTLiBb9fzOZ-oK44/preview)', 'taskgroups': [ next(tg['TaskGroup'] for tg in taskgroups if tg['taskgroup'] == 'Memorial Scholarship'), ], }, {'task': 'Annual Report', 'priority': 3, 'dateofyear': '05-01', 'expirystarts': 9, 'expirystarts_units': DATE_UNIT_MONTHS, 'isoptional': False, 'expirysoon': 4, 'expirysoon_units': DATE_UNIT_WEEKS, 'description': 'Issue Annual Report for review', 'taskgroups': [ next(tg['TaskGroup'] for tg in taskgroups if tg['taskgroup'] == 'President') ], }, {'task': 'Q1 President\'s Message', 'priority': 3, 'dateofyear': '03-01', 'expirystarts': 6, 'expirystarts_units': DATE_UNIT_MONTHS, 'isoptional': False, 'expirysoon': 2, 'expirysoon_units': DATE_UNIT_WEEKS, 'description': 'Write Q1 President\'s Message for Intervals Blog', 'taskgroups': [ next(tg['TaskGroup'] for tg in taskgroups if tg['taskgroup'] == 'President') ], }, {'task': 'Q2 President\'s Message', 'priority': 3, 'dateofyear': '06-01', 'expirystarts': 6, 'expirystarts_units': DATE_UNIT_MONTHS, 'isoptional': False, 'expirysoon': 2, 'expirysoon_units': DATE_UNIT_WEEKS, 'description': 'Write Q2 President\'s Message for Intervals Blog', 'taskgroups': [ next(tg['TaskGroup'] for tg in taskgroups if tg['taskgroup'] == 'President') ], }, {'task': 'Q3 President\'s Message', 'priority': 3, 'dateofyear': '09-01', 'expirystarts': 6, 'expirystarts_units': DATE_UNIT_MONTHS, 'isoptional': False, 'expirysoon': 2, 'expirysoon_units': DATE_UNIT_WEEKS, 'description': 'Write Q3 President\'s Message for Intervals Blog', 'taskgroups': [ next(tg['TaskGroup'] for tg in taskgroups if tg['taskgroup'] == 'President') ], }, {'task': 'Q4 President\'s Message', 'priority': 3, 'dateofyear': '12-01', 'expirystarts': 9, 'expirystarts_units': DATE_UNIT_MONTHS, 'isoptional': False, 'expirysoon': 4, 'expirysoon_units': DATE_UNIT_WEEKS, 'description': 'Write Q4 President\'s Message for Intervals Blog', 'taskgroups': [ next(tg['TaskGroup'] for tg in taskgroups if tg['taskgroup'] == 'President') ], }, {'task': 'Financial Policies', 'priority': 3, 'period': 2, 'period_units': DATE_UNIT_YEARS, 'isoptional': False, 'expirysoon': 2, 'expirysoon_units': DATE_UNIT_WEEKS, 'description': 'Review ' '[Financial Policies](https://docs.google.com/document/d/1Aa8NCSj20Pb9FgLJB2Q82m9_SLlAc3qBYiFY9sGUzz0/edit)', 'taskgroups': [ next(tg['TaskGroup'] for tg in taskgroups if tg['taskgroup'] == 'Finances') ], }, {'task': 'Sales Tax Exemption', 'priority': 3, 'period': 2, 'period_units': DATE_UNIT_YEARS, 'isoptional': False, 'expirysoon': 2, 'expirysoon_units': DATE_UNIT_WEEKS, 'description': 'Renew sales tax exemption', 'taskgroups': [ next(tg['TaskGroup'] for tg in taskgroups if tg['taskgroup'] == 'Finances') ], }, {'task': 'Annual Budget', 'priority': 3, 'dateofyear': '02-01', 'expirystarts': 9, 'expirystarts_units': DATE_UNIT_MONTHS, 'isoptional': False, 'expirysoon': 4, 'expirysoon_units': DATE_UNIT_WEEKS, 'description': 'Manage and develop the proposed annual budget', 'taskgroups': [ next(tg['TaskGroup'] for tg in taskgroups if tg['taskgroup'] == 'Finances') ], }, {'task': '1099-MISC to Subcontractors', 'priority': 3, 'dateofyear': '01-16', 'expirystarts': 9, 'expirystarts_units': DATE_UNIT_MONTHS, 'isoptional': False, 'expirysoon': 4, 'expirysoon_units': DATE_UNIT_WEEKS, 'description': 'Issue contractor 1099-MISC to subcontractors', 'taskgroups': [ next(tg['TaskGroup'] for tg in taskgroups if tg['taskgroup'] == 'Finances') ], }, {'task': '990 Tax Return', 'priority': 3, 'dateofyear': '04-01', 'expirystarts': 9, 'expirystarts_units': DATE_UNIT_MONTHS, 'isoptional': False, 'expirysoon': 4, 'expirysoon_units': DATE_UNIT_WEEKS, 'description': 'File 990 Tax Return', 'taskgroups': [ next(tg['TaskGroup'] for tg in taskgroups if tg['taskgroup'] == 'Finances') ], }, {'task': 'Maryland Property Taxes', 'priority': 3, 'dateofyear': '04-15', 'expirystarts': 9, 'expirystarts_units': DATE_UNIT_MONTHS, 'isoptional': False, 'expirysoon': 4, 'expirysoon_units': DATE_UNIT_WEEKS, 'description': 'File Maryland State Annual Report and Personal Property Tax Return', 'taskgroups': [ next(tg['TaskGroup'] for tg in taskgroups if tg['taskgroup'] == 'Finances') ], }, {'task': 'Update Maryland Charitable Fundraising Registration', 'priority': 3, 'dateofyear': '04-15', 'expirystarts': 9, 'expirystarts_units': DATE_UNIT_MONTHS, 'isoptional': False, 'expirysoon': 4, 'expirysoon_units': DATE_UNIT_WEEKS, 'description': 'File Maryland State Annual Update of Registration', 'taskgroups': [ next(tg['TaskGroup'] for tg in taskgroups if tg['taskgroup'] == 'Finances') ], }, {'task': 'Sales Tax H1', 'priority': 3, 'dateofyear': '06-20', 'expirystarts': 9, 'expirystarts_units': DATE_UNIT_MONTHS, 'isoptional': False, 'expirysoon': 4, 'expirysoon_units': DATE_UNIT_WEEKS, 'description': 'File H1 Sales Tax Return', 'taskgroups': [ next(tg['TaskGroup'] for tg in taskgroups if tg['taskgroup'] == 'Finances') ], }, {'task': 'Sales Tax H2', 'priority': 3, 'dateofyear': '01-20', 'expirystarts': 9, 'expirystarts_units': DATE_UNIT_MONTHS, 'isoptional': False, 'expirysoon': 4, 'expirysoon_units': DATE_UNIT_WEEKS, 'description': 'File H2 Sales Tax Return', 'taskgroups': [ next(tg['TaskGroup'] for tg in taskgroups if tg['taskgroup'] == 'Finances') ], }, {'task': 'Frederick City Property Taxes', 'priority': 3, 'dateofyear': '07-31', 'expirystarts': 9, 'expirystarts_units': DATE_UNIT_MONTHS, 'isoptional': False, 'expirysoon': 4, 'expirysoon_units': DATE_UNIT_WEEKS, 'description': 'File Frederick City Personal Property Tax Return', 'taskgroups': [ next(tg['TaskGroup'] for tg in taskgroups if tg['taskgroup'] == 'Finances') ], }, {'task': 'RRCA Annual Dues, etc', 'priority': 3, 'dateofyear': '12-31', 'expirystarts': 9, 'expirystarts_units': DATE_UNIT_MONTHS, 'isoptional': False, 'expirysoon': 4, 'expirysoon_units': DATE_UNIT_WEEKS, 'description': 'Renew RRCA Annual Dues, Insurance and Music Licensing', 'taskgroups': [ next(tg['TaskGroup'] for tg in taskgroups if tg['taskgroup'] == 'Finances') ], }, {'task': 'RRCA Property Insurance', 'priority': 3, 'dateofyear': '12-31', 'expirystarts': 9, 'expirystarts_units': DATE_UNIT_MONTHS, 'isoptional': False, 'expirysoon': 4, 'expirysoon_units': DATE_UNIT_WEEKS, 'description': 'Renew RRCA Property Insurance', 'taskgroups': [ next(tg['TaskGroup'] for tg in taskgroups if tg['taskgroup'] == 'Finances') ], }, {'task': 'Annual FSRC Race Calendar', 'priority': 3, 'dateofyear': '11-30', 'expirystarts': 9, 'expirystarts_units': DATE_UNIT_MONTHS, 'isoptional': False, 'expirysoon': 4, 'expirysoon_units': DATE_UNIT_WEEKS, 'description': 'Submit annual race calendar to the Board', 'taskgroups': [ next(tg['TaskGroup'] for tg in taskgroups if tg['taskgroup'] == 'Races') ], }, {'task': 'RRCA Race Calendar', 'priority': 3, 'dateofyear': '01-31', 'expirystarts': 9, 'expirystarts_units': DATE_UNIT_MONTHS, 'isoptional': False, 'expirysoon': 4, 'expirysoon_units': DATE_UNIT_WEEKS, 'description': 'Update RRCA Race Calendar', 'taskgroups': [ next(tg['TaskGroup'] for tg in taskgroups if tg['taskgroup'] == 'Races') ], }, {'task': 'Technology Roles and Responsibilities', 'priority': 5, 'period': 2, 'period_units': DATE_UNIT_YEARS, 'isoptional': False, 'expirysoon': 2, 'expirysoon_units': DATE_UNIT_WEEKS, 'description': 'Review ' '[Technology Roles and Responsibilities](https://docs.google.com/document/d/1p73d2aPZ5ws8Ooul5JAgQYZXMAL2j2mj9NqXJkgpGY8/preview)', 'taskgroups': [ next(tg['TaskGroup'] for tg in taskgroups if tg['taskgroup'] == 'Technology Committee Leadership') ], }, {'task': 'Membership Roles and Responsibilities', 'priority': 5, 'period': 2, 'period_units': DATE_UNIT_YEARS, 'isoptional': False, 'expirysoon': 2, 'expirysoon_units': DATE_UNIT_WEEKS, 'description': 'Review ' '[Membership Roles and Responsibilities](https://docs.google.com/document/d/1VAGcgKhi4EVB_vYN2-Q4cxC6x1-0mIlYe5wnYClOtMU/preview)', 'taskgroups': [ next(tg['TaskGroup'] for tg in taskgroups if tg['taskgroup'] == 'Membership Committee Leadership') ], }, {'task': 'Communications Roles and Responsibilities', 'priority': 5, 'period': 2, 'period_units': DATE_UNIT_YEARS, 'isoptional': False, 'expirysoon': 2, 'expirysoon_units': DATE_UNIT_WEEKS, 'description': 'Review ' '[Communications Roles and Responsibilities](https://docs.google.com/document/d/1F5-k1fqz96xanu_ckI_DfT3UllNiPBrw7pmHz8MNYWA/preview)', 'taskgroups': [ next(tg['TaskGroup'] for tg in taskgroups if tg['taskgroup'] == 'Communications Committee Leadership') ], }, ] for thetask in tasks: fields = thetask.pop('fields',[]) thistask = Task(interest=localfsrcinterest, **thetask) db.session.add(thistask) db.session.flush() for field in fields: need, taskfieldname = field.split('/') tasktaskfield = TaskTaskField(need=need, taskfield=TaskField.query.filter_by(fieldname=taskfieldname).one()) thistask.fields.append(tasktaskfield) positions = [ {'position': 'President', 'taskgroups': [ next(tg['TaskGroup'] for tg in taskgroups if tg['taskgroup'] == 'President'), next(tg['TaskGroup'] for tg in taskgroups if tg['taskgroup'] == 'Executive Officer'), ], }, {'position': 'Vice President', 'taskgroups': [ next(tg['TaskGroup'] for tg in taskgroups if tg['taskgroup'] == 'Executive Officer'), ], }, {'position': 'Treasurer', 'taskgroups': [ next(tg['TaskGroup'] for tg in taskgroups if tg['taskgroup'] == 'Executive Officer'), next(tg['TaskGroup'] for tg in taskgroups if tg['taskgroup'] == 'Finances'), next(tg['TaskGroup'] for tg in taskgroups if tg['taskgroup'] == 'Bank Account Full Access'), ], }, {'position': 'Secretary', 'taskgroups': [ next(tg['TaskGroup'] for tg in taskgroups if tg['taskgroup'] == 'Executive Officer'), ], }, {'position': 'Reviewing Officer', 'description': 'Performs regular (monthly) review of financial records', 'taskgroups': [ next(tg['TaskGroup'] for tg in taskgroups if tg['taskgroup'] == 'Bank Account Full Access'), ], }, {'position': 'Director', 'taskgroups': [ next(tg['TaskGroup'] for tg in taskgroups if tg['taskgroup'] == 'Board of Directors'), ], }, {'position': 'Training Committee Chair', 'taskgroups': [ next(tg['TaskGroup'] for tg in taskgroups if tg['taskgroup'] == 'Board Meeting Attendee'), next(tg['TaskGroup'] for tg in taskgroups if tg['taskgroup'] == 'General Committee Leadership'), next(tg['TaskGroup'] for tg in taskgroups if tg['taskgroup'] == 'Training'), ], }, {'position': 'Training Coach', 'taskgroups': [ next(tg['TaskGroup'] for tg in taskgroups if tg['taskgroup'] == 'Training'), ], }, {'position': 'Youth Training Coach', 'taskgroups': [ next(tg['TaskGroup'] for tg in taskgroups if tg['taskgroup'] == 'Training'), next(tg['TaskGroup'] for tg in taskgroups if tg['taskgroup'] == 'Youth Training'), ], }, {'position': 'Races Committee Chair', 'taskgroups': [ next(tg['TaskGroup'] for tg in taskgroups if tg['taskgroup'] == 'Board Meeting Attendee'), next(tg['TaskGroup'] for tg in taskgroups if tg['taskgroup'] == 'General Committee Leadership'), next(tg['TaskGroup'] for tg in taskgroups if tg['taskgroup'] == 'Races'), next(tg['TaskGroup'] for tg in taskgroups if tg['taskgroup'] == 'Signature Race Director'), next(tg['TaskGroup'] for tg in taskgroups if tg['taskgroup'] == 'Low Key Race Director'), ], }, {'position': 'Market Street Mile Race Director', 'taskgroups': [ next(tg['TaskGroup'] for tg in taskgroups if tg['taskgroup'] == 'Signature Race Director'), ], }, {'position': 'Summer Solstice 8K Race Director', 'taskgroups': [ next(tg['TaskGroup'] for tg in taskgroups if tg['taskgroup'] == 'Signature Race Director'), ], }, {'position': 'Women\'s Distance Festival Race Director', 'taskgroups': [ next(tg['TaskGroup'] for tg in taskgroups if tg['taskgroup'] == 'Signature Race Director'), ], }, {'position': 'Rick\'s Run Race Director', 'taskgroups': [ next(tg['TaskGroup'] for tg in taskgroups if tg['taskgroup'] == 'Signature Race Director'), ], }, {'position': 'Lewis Run Race Director', 'taskgroups': [ next(tg['TaskGroup'] for tg in taskgroups if tg['taskgroup'] == 'Low Key Race Director'), ], }, {'position': 'Independence 5000 Race Director', 'taskgroups': [ next(tg['TaskGroup'] for tg in taskgroups if tg['taskgroup'] == 'Low Key Race Director'), ], }, {'position': 'Pie Run Race Director', 'taskgroups': [ next(tg['TaskGroup'] for tg in taskgroups if tg['taskgroup'] == 'Low Key Race Director'), ], }, {'position': 'Li\'l Bennet 5K Race Director', 'taskgroups': [ next(tg['TaskGroup'] for tg in taskgroups if tg['taskgroup'] == 'Low Key Race Director'), ], }, {'position': 'FSRC Memorial Scholarship Review Board Chair', 'taskgroups': [ next(tg['TaskGroup'] for tg in taskgroups if tg['taskgroup'] == 'Board Meeting Attendee'), next(tg['TaskGroup'] for tg in taskgroups if tg['taskgroup'] == 'Memorial Scholarship'), next(tg['TaskGroup'] for tg in taskgroups if tg['taskgroup'] == 'General Committee Leadership'), ], }, {'position': 'Nominating Committee Member', 'taskgroups': [ next(tg['TaskGroup'] for tg in taskgroups if tg['taskgroup'] == 'Nomination Process'), ], }, {'position': 'Technology Chair', 'taskgroups': [ next(tg['TaskGroup'] for tg in taskgroups if tg['taskgroup'] == 'Board Meeting Attendee'), next(tg['TaskGroup'] for tg in taskgroups if tg['taskgroup'] == 'Technology Committee Leadership'), ], }, {'position': 'Membership Chair', 'taskgroups': [ next(tg['TaskGroup'] for tg in taskgroups if tg['taskgroup'] == 'Board Meeting Attendee'), next(tg['TaskGroup'] for tg in taskgroups if tg['taskgroup'] == 'Membership Committee Leadership'), ], }, {'position': 'Communications Chair', 'taskgroups': [ next(tg['TaskGroup'] for tg in taskgroups if tg['taskgroup'] == 'Board Meeting Attendee'), next(tg['TaskGroup'] for tg in taskgroups if tg['taskgroup'] == 'Communications Committee Leadership'), ], }, {'position': 'Store Chair', 'taskgroups': [ next(tg['TaskGroup'] for tg in taskgroups if tg['taskgroup'] == 'Board Meeting Attendee'), next(tg['TaskGroup'] for tg in taskgroups if tg['taskgroup'] == 'General Committee Leadership'), ], }, {'position': 'Race Services Chair', 'taskgroups': [ next(tg['TaskGroup'] for tg in taskgroups if tg['taskgroup'] == 'Board Meeting Attendee'), next(tg['TaskGroup'] for tg in taskgroups if tg['taskgroup'] == 'General Committee Leadership'), ], }, {'position': 'Competition Chair', 'taskgroups': [ next(tg['TaskGroup'] for tg in taskgroups if tg['taskgroup'] == 'Board Meeting Attendee'), next(tg['TaskGroup'] for tg in taskgroups if tg['taskgroup'] == 'General Committee Leadership'), ], }, {'position': 'Community Liaison Chair', 'taskgroups': [ next(tg['TaskGroup'] for tg in taskgroups if tg['taskgroup'] == 'Board Meeting Attendee'), next(tg['TaskGroup'] for tg in taskgroups if tg['taskgroup'] == 'General Committee Leadership'), ], }, {'position': 'Racing Team Chair', 'taskgroups': [ next(tg['TaskGroup'] for tg in taskgroups if tg['taskgroup'] == 'Board Meeting Attendee'), next(tg['TaskGroup'] for tg in taskgroups if tg['taskgroup'] == 'General Committee Leadership'), ], }, {'position': 'Volunteer Appreciation Chair', 'taskgroups': [ next(tg['TaskGroup'] for tg in taskgroups if tg['taskgroup'] == 'Board Meeting Attendee'), next(tg['TaskGroup'] for tg in taskgroups if tg['taskgroup'] == 'General Committee Leadership'), ], }, {'position': 'Panther\'s Head Coach', 'taskgroups': [ next(tg['TaskGroup'] for tg in taskgroups if tg['taskgroup'] == 'Board Meeting Attendee'), next(tg['TaskGroup'] for tg in taskgroups if tg['taskgroup'] == 'Youth Training'), next(tg['TaskGroup'] for tg in taskgroups if tg['taskgroup'] == 'Training'), ], }, {'position': 'Spires Head Coach', 'taskgroups': [ next(tg['TaskGroup'] for tg in taskgroups if tg['taskgroup'] == 'Board Meeting Attendee'), next(tg['TaskGroup'] for tg in taskgroups if tg['taskgroup'] == 'Youth Training'), next(tg['TaskGroup'] for tg in taskgroups if tg['taskgroup'] == 'Training'), ], }, {'position': 'Social Chair', 'taskgroups': [ next(tg['TaskGroup'] for tg in taskgroups if tg['taskgroup'] == 'Board Meeting Attendee'), next(tg['TaskGroup'] for tg in taskgroups if tg['taskgroup'] == 'General Committee Leadership'), ], }, ] for position in positions: if 'description' not in position: position['description'] = position['position'] thisposition = Position(interest=localfsrcinterest, **position) db.session.add(thisposition) db.session.flush() db.session.commit()
def main(): descr = ''' Update racing team info volunteer records from csv file ''' parser = ArgumentParser(description=descr) parser.add_argument('inputfile', help='csv file with input records', default=None) args = parser.parse_args() scriptdir = dirname(__file__) # two levels up scriptfolder = dirname(dirname(scriptdir)) configdir = join(scriptfolder, 'config') memberconfigfile = "members.cfg" memberconfigpath = join(configdir, memberconfigfile) userconfigfile = "users.cfg" userconfigpath = join(configdir, userconfigfile) # create app and get configuration # use this order so members.cfg overrrides users.cfg configfiles = [userconfigpath, memberconfigpath] app = create_app(Development(configfiles), configfiles) # set up database db.init_app(app) # determine input file encoding with open(args.inputfile, 'rb') as binaryfile: rawdata = binaryfile.read() detected = detect(rawdata) # need app context, open input file with app.app_context(), open(args.inputfile, 'r', encoding=detected['encoding'], newline='', errors='replace') as IN: # turn on logging setlogging() # trick local interest stuff g.interest = 'fsrc' # initialize database tables from input file infile = DictReader(IN) for row in infile: # first check if racing team member exists localuser = LocalUser.query.filter_by(name=row['name'], **localinterest_query_params()).one_or_none() member = RacingTeamMember.query.filter_by(localuser=localuser, **localinterest_query_params()).one_or_none() if localuser else None if not member: continue # this pulls timezone information off of timestamp, formatted like 'Sun Feb 25 2018 14:07:17 GMT-0500 (EST)' timestampasc = ' '.join(row['timestamp'].split(' ')[:-2]) timestamp = tstamp.asc2dt(timestampasc) # if we already have received an info record for this member at this timestamp, skip it else we'll get duplicates inforec = RacingTeamInfo.query.filter_by(member=member, logtime=timestamp).one_or_none() if inforec: continue # if we've gotten here, we need to add info and volunteer records inforec = RacingTeamInfo(interest=localinterest(), member=member, logtime=timestamp) db.session.add(inforec) volrec = RacingTeamVolunteer( interest=localinterest(), info=inforec, eventdate = isodate.asc2dt(row['eventdate']).date(), eventname = row['eventname'], hours = row['hours'], comment = row['comments'], ) db.session.add(volrec) db.session.commit()
pass scriptdir = dirname(__file__) # two levels up scriptfolder = dirname(dirname(scriptdir)) configdir = join(scriptfolder, 'config') memberconfigfile = "members.cfg" memberconfigpath = join(configdir, memberconfigfile) userconfigfile = "users.cfg" userconfigpath = join(configdir, userconfigfile) # create app and get configuration # use this order so members.cfg overrrides users.cfg configfiles = [userconfigpath, memberconfigpath] app = create_app(Development(configfiles), configfiles) # set up database db.init_app(app) # set up scoped session with app.app_context(): # turn on logging setlogging() # clear and initialize the members database # bind=None per https://flask-sqlalchemy.palletsprojects.com/en/2.x/binds/ db.drop_all(bind=None) db.create_all(bind=None) update_local_tables()
def main(): descr = ''' Update racing team info volunteer records from csv file ''' parser = ArgumentParser(description=descr) parser.add_argument('inputfile', help='csv file with input records', default=None) args = parser.parse_args() scriptdir = dirname(__file__) # two levels up scriptfolder = dirname(dirname(scriptdir)) configdir = join(scriptfolder, 'config') memberconfigfile = "members.cfg" memberconfigpath = join(configdir, memberconfigfile) userconfigfile = "users.cfg" userconfigpath = join(configdir, userconfigfile) # create app and get configuration # use this order so members.cfg overrrides users.cfg configfiles = [userconfigpath, memberconfigpath] app = create_app(Development(configfiles), configfiles) # set up database db.init_app(app) # determine input file encoding with open(args.inputfile, 'rb') as binaryfile: rawdata = binaryfile.read() detected = detect(rawdata) # translate type from old format to new applntype = { 'Returning Racing Team Member': 'renewal', 'New Racing Team Member': 'new', } # need app context, open input file with app.app_context(), open(args.inputfile, 'r', encoding=detected['encoding'], newline='', errors='replace') as IN: # turn on logging setlogging() # trick local interest stuff g.interest = 'fsrc' # initialize database tables from input file infile = DictReader(IN) for row in infile: # this pulls timezone information off of record timestamp, formatted like 'Sun Feb 25 2018 14:07:17 GMT-0500 (EST)' timestampasc = ' '.join(row['time'].split(' ')[:-2]) timestamp = tstamp.asc2dt(timestampasc) # if we already have received an application for this name at this timestamp, skip it else we'll get duplicates applnrec = RacingTeamApplication.query.filter_by( name=row['name'], logtime=timestamp, **localinterest_query_params()).one_or_none() if applnrec: continue # at least one record doesn't have a date of birth if not row['dob']: app.logger.warning( f"racingteam_appln_init: skipping {row['name']} {row['race1-name']} {row[f'race1-date']}" ) continue # if we've gotten here, we need to add application and result records dob = isodate.asc2dt(row['dob']).date() applnrec = RacingTeamApplication( interest=localinterest(), logtime=timestamp, name=row['name'], type=applntype[row['applntype']], comments=row['comments'], dateofbirth=dob, email=row['email'], gender=row['gender'].upper()[0], ) db.session.add(applnrec) for race in ['race1', 'race2']: # originally, new members were only asked for one race # detect this condition and skip this result -- this should only happen for race2 if not row[f'{race}-date']: continue # handle case where age grade was not calculated properly # this was due to deficiency in the original script, so these should be early entries # it's not worth adding the complexity to fix this data at this point try: agegrade = float(row[f'{race}-agegrade']), agegrade = row[f'{race}-agegrade'] except ValueError: agegrade = None # calculate age racedate = isodate.asc2dt(row[f'{race}-date']).date() thisage = age(racedate, dob) # add result resultrec = RacingTeamResult( interest=localinterest(), application=applnrec, eventdate=racedate, eventname=row[f'{race}-name'], age=thisage, agegrade=agegrade, distance=row[f'{race}-distance'], units=row[f'{race}-units'], location=row[f'{race}-location'], url=row[f'{race}-resultslink'], time=row[f'{race}-time'], ) db.session.add(resultrec) db.session.commit()
def main(): scriptdir = dirname(__file__) # two levels up scriptfolder = dirname(dirname(scriptdir)) configdir = join(scriptfolder, 'config') memberconfigfile = "members.cfg" memberconfigpath = join(configdir, memberconfigfile) userconfigfile = "users.cfg" userconfigpath = join(configdir, userconfigfile) # create app and get configuration # use this order so members.cfg overrrides users.cfg configfiles = [userconfigpath, memberconfigpath] app = create_app(Development(configfiles), configfiles) # set up database db.init_app(app) # set up scoped session with app.app_context(): parser = ArgumentParser() parser.add_argument('--noclear', default=False, action='store_const', const=True, help='use --nomembers to skip members') args = parser.parse_args() # clear and initialize the members database # bind=None per https://flask-sqlalchemy.palletsprojects.com/en/2.x/binds/ if not args.noclear: db.drop_all(bind=None) db.create_all(bind=None) update_local_tables() testuser = User.query.filter_by(email='*****@*****.**').one() testinterest = Interest.query.filter_by(interest='fsrc').one() localtestinterest = LocalInterest.query.filter_by(interest_id=testinterest.id).one() localtestuser = LocalUser.query.filter_by(user_id=testuser.id, interest_id=localtestinterest.id).one() localtestinterest.initial_expiration = date(2020, 4, 1) localtestinterest.from_email = "*****@*****.**" eventaskgroup = TaskGroup(taskgroup='Even Tasks', description='even tasks description', interest=localtestinterest) db.session.add(eventaskgroup) oddtaskgroup = TaskGroup(taskgroup='Odd Tasks', description='odd tasks description', interest=localtestinterest) db.session.add(oddtaskgroup) doytaskgroup = TaskGroup(taskgroup='Date of Year Tasks', description='date of year tasks', interest=localtestinterest) db.session.add(oddtaskgroup) db.session.flush() eventaskgroup.users.append(localtestuser) oddtaskgroup.users.append(localtestuser) doytaskgroup.users.append(localtestuser) priority = 1 for fieldtype in input_type_all: fieldname = gen_fieldname() thisfield = TaskField(taskfield='test {}'.format(fieldtype), fieldname=fieldname, interest=localtestinterest, inputtype=fieldtype, priority=priority, displaylabel='Display Label {}'.format(fieldtype), uploadurl=(url_for('admin.fieldupload', interest='fsrc') + '?{}={}'.format(FIELDNAME_ARG, fieldname) if fieldtype==INPUT_TYPE_UPLOAD else None) ) if fieldtype == INPUT_TYPE_DATE: thisfield.override_completion = True db.session.add(thisfield) db.session.flush() thistask = Task(task='Task {}'.format(fieldtype), interest=localtestinterest, description='Task description for {}'.format(fieldtype), period=1, period_units=DATE_UNIT_YEARS, expirysoon=4, expirysoon_units=DATE_UNIT_WEEKS, isoptional=False, priority=priority, # fields=[thisfield], ) task_taskfield = TaskTaskField(need='required', taskfield=thisfield) thistask.fields.append(task_taskfield) db.session.add(thistask) db.session.flush() if int(priority) % 2 == 0: eventaskgroup.tasks.append(thistask) else: oddtaskgroup.tasks.append(thistask) priority += 1 doytests = [ '01-01', '03-01', '05-01', '07-01', '09-01', '11-01', ] compls = [None, '01-02', '06-02', '12-15'] today = date.today() todaymmdd = today.isoformat()[-5:] todayplus = today + timedelta(10) compls.append(todayplus.isoformat()[-5:]) do_doytests = True if do_doytests: for doytest in doytests: for compl in compls: tasktext = 'Task compl {} (for {})'.format(compl, doytest) thistask = Task(task=tasktext, description='Task compl {} (for {})'.format(compl, doytest), interest=localtestinterest, dateofyear=doytest, isoptional=False, priority=1, expirystarts=9, expirystarts_units=DATE_UNIT_MONTHS, expirysoon=8, expirysoon_units=DATE_UNIT_WEEKS, ) db.session.add(thistask) db.session.flush() doytaskgroup.tasks.append(thistask) taskmonth, taskday = [int(md) for md in doytest.split('-')] if compl: complmonth, complday = [int(md) for md in compl.split('-')] if compl <= todaymmdd: year = today.year else: year = today.year - 1 taskcompletion = TaskCompletion( interest=localtestinterest, task=thistask, user=localtestuser, update_time = datetime.now(), updated_by=localtestuser.id, completion = datetime(year, complmonth, complday) ) db.session.add(taskcompletion) db.session.commit()
# homegrown from members import create_app from members.settings import Production from members.model import db from members.applogging import setlogging from scripts import Members, Membership abspath = os.path.abspath(__file__) configpath = os.path.join(os.path.dirname(abspath), 'config', 'members.cfg') userconfigpath = os.path.join(os.path.dirname(abspath), 'config', 'users.cfg') # userconfigpath first so configpath can override configfiles = [userconfigpath, configpath] # init_for_operation=False because when we create app this would use database and cause # sqlalchemy.exc.OperationalError if one of the updating tables needs migration app = create_app(Production(configfiles), configfiles, init_for_operation=False) # set up scoped session with app.app_context(): # this causes SQLALCHEMY_BINDS not to work ('user' bind missing) # db.session = scoped_session(sessionmaker(autocommit=False, autoflush=False, bind=db.engine)) # db.query = db.session.query_property() # turn on logging setlogging() migrate = Migrate(app, db, compare_type=True) members = Members(app, db) membership = Membership(app, db)