예제 #1
0
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()
예제 #2
0
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()
예제 #3
0
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()
예제 #4
0
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)
예제 #5
0
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()
예제 #6
0
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()
예제 #7
0
    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()
예제 #8
0
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()
예제 #9
0
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()
예제 #10
0
파일: app.py 프로젝트: louking/members
# 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)