예제 #1
0
    def testLastTaskId(self):
        # Using "_" with no prior task activity should raise an exception   
        self.assertRaises(YokadiException, self.cmd.getTaskFromId, "_")

        tui.addInputAnswers("y")
        self.cmd.do_t_add("x t1")
        task1 = Task.get(1)
        self.assertEqual(self.cmd.getTaskFromId("_"), task1)

        self.cmd.do_t_add("x t2")
        task2 = Task.get(2)
        self.assertEqual(self.cmd.getTaskFromId("_"), task2)

        self.cmd.do_t_mark_started("1")
        self.assertEqual(self.cmd.getTaskFromId("_"), task1)
예제 #2
0
    def testRecurs(self):
        tui.addInputAnswers("y")
        self.cmd.do_t_add("x t1")
        task = Task.get(1)
        self.cmd.do_t_recurs("1 daily 10:00")
        desc = str(task.recurrence)
        self.cmd.do_t_recurs("1 weekly FR 23:00")
        self.cmd.do_t_recurs("1 none")
        self.cmd.do_t_recurs("1 weekly fr 23:00")
        self.cmd.do_t_recurs("1 weekly Fr 23:00")
        self.cmd.do_t_recurs("1 weekly Friday 23:00")
        self.cmd.do_t_recurs("1 monthly 3 13:00")
        self.cmd.do_t_recurs("1 monthly second friday 13:00")
        self.cmd.do_t_recurs("1 yearly 3/07 11:20")
        self.cmd.do_t_recurs("1 quarterly 14 11:20")
        self.cmd.do_t_recurs("1 quarterly first monday 23:20")
        self.assertNotEqual(desc, str(task.recurrence))
        self.assertEqual(task.status, "new")
        self.cmd.do_t_mark_done("1")
        self.assertEqual(task.status, "new")

        for bad_input in ("", # No task
                          "1", # No recurence
                          "1 foo", # Unknown recurrence
                          "1 daily", # No time
                          "1 weekly", # No day
                          "1 weekly monday", # No time
                          "1 monthly", # No day
                          "1 monthly 10", # No time
                          "1 quarterly", # No day
                          "1 quarterly 10", # No time
                          "1 monthly foo 12:00", # Bad date
                          ):
            self.assertRaises(YokadiException, self.cmd.do_t_recurs, bad_input)
예제 #3
0
def handle_file(code, task, file):
    task_obj = Task.get(name=task)
    data = get_data_for_code(code)
    if task_obj is None:
        return {"error": "Report type not found: " + str(task)}, 404

    committee = data["group"]["codeName"]
    year = str(data["year"])
    lp = str(data["study_period"])

    name = task + "_" + committee + "_" + year + "_" + lp + ".pdf"
    path = "./uploads/" + year + "/lp" + lp + "/" + str(
        data["meeting_no"]) + "/" + str(committee)

    if not os.path.exists(path):
        os.makedirs(path)

    save_loc = path + "/" + name

    print("Saving file " + str(file) + " in " + path)
    file.save(save_loc)

    code_file = CodeFile.get(code=code, task=task)
    if code_file is None:
        CodeFile(code=code, task=task, file_location=save_loc)
        return {"overwrite": False}
    else:
        print("OVERWRITE!")
        code_file.date = datetime.datetime.now()
        return {"overwrite": True}
예제 #4
0
파일: taskcmd.py 프로젝트: ryanakca/yokadi
    def do_t_reorder(self, line):
        """Reorder tasks of a project.
        It works by starting an editor with the task list: you can then change
        the order of the lines and save the list. The urgency field will be
        updated to match the order.
        t_reorder <project_name>"""
        project = Project.byName(projectName)
        taskList = Task.select(AND(Task.q.projectID == project.id,
                                   Task.q.status    != 'done'),
                               orderBy=-Task.q.urgency)
        lines = [ "%d,%s" % (x.id, x.title) for x in taskList]
        text = tui.editText("\n".join(lines))

        ids = []
        for line in text.split("\n"):
            line = line.strip()
            if not "," in line:
                continue
            id = int(line.split(",")[0])
            ids.append(id)

        ids.reverse()
        for urgency, id in enumerate(ids):
            task = Task.get(id)
            task.urgency = urgency
예제 #5
0
 def testMark(self):
     tui.addInputAnswers("y")
     self.cmd.do_t_add("x t1")
     task = Task.get(1)
     self.assertEqual(task.status, "new")
     self.cmd.do_t_mark_started("1")
     self.assertEqual(task.status, "started")
     self.cmd.do_t_mark_done("1")
     self.assertEqual(task.status, "done")
예제 #6
0
    def testAddKeywords(self):
        tui.addInputAnswers("y")
        self.cmd.do_t_add("x t1")
        task = Task.get(1)

        tui.addInputAnswers("y", "y")
        self.cmd.do_t_add_keywords("1 @kw1 @kw2=12")

        kwDict = task.getKeywordDict()
        self.assertEqual(kwDict, dict(kw1=None, kw2=12))
예제 #7
0
    def testAdd(self):
        tui.addInputAnswers("y", "2", "4", "123")
        self.cmd.do_bug_add("x t1")

        tasks = list(Task.select())
        result = [x.title for x in tasks]
        expected = [u"t1"]
        self.assertEqual(result, expected)

        kwDict = Task.get(1).getKeywordDict()
        self.assertEqual(kwDict, dict(_severity=2, _likelihood=4, _bug=123))
예제 #8
0
    def testAdd(self):
        tui.addInputAnswers("y")
        self.cmd.do_t_add("x t1")

        tui.addInputAnswers("y", "y")
        self.cmd.do_t_add("x @kw1 @kw2=12 t2")

        tasks = list(Task.select())
        result = [x.title for x in tasks]
        expected = [u"t1", u"t2"]
        self.assertEqual(result, expected)

        kwDict = Task.get(2).getKeywordDict()
        self.assertEqual(kwDict, dict(kw1=None, kw2=12))
예제 #9
0
    def testAddKeywords(self):
        tui.addInputAnswers("y")
        self.cmd.do_t_add("x t1")
        task = Task.get(1)

        tui.addInputAnswers("y", "y")
        self.cmd.do_t_add_keywords("1 @kw1 @kw2=12")

        kwDict = task.getKeywordDict()
        self.assertEqual(kwDict, dict(kw1=None, kw2=12))

        for bad_input in ("", # No task
                          "1", # No keyword
                          "1 kw1"): # No @ before kw1
            self.assertRaises(YokadiException, self.cmd.do_t_add_keywords, bad_input)
예제 #10
0
def get_or_create_task_for_user(user, date=None, ip=None):
    if not date:
        date = today()
    try:
        task_obj = Task.get(Task.user == user, Task.day == date)
        if task_obj.task not in get_tasks(user.level):
            task_obj.task = random_task_for_user(user)
            task_obj.save()
    except Task.DoesNotExist:
        if ip:
            task_name = random_task_for_ip(ip)
        else:
            task_name = random_task_for_user(user)
        task_obj = Task(user=user, day=date, task=task_name)
        task_obj.save()
    return task_obj
예제 #11
0
def changeset():
    if 'osm_token' not in session:
        redirect(url_for('login'))

    cs_data = request.args.get('changeset')
    if not cs_data.strip():
        return redirect(url_for('front'))
    user = get_user()
    # TODO: call submit_changeset instead
    try:
        changeset = parse_changeset_id(cs_data)
        cs_date, conforms = validate_changeset(user, changeset, None,
                                               openstreetmap)
    except ValueError as e:
        flash(str(e))
        return redirect(url_for('front'))
    if not cs_date or cs_date != today():
        flash('Date of the changeset is wrong')
        return redirect(url_for('front'))
    task = Task.get(Task.user == user, Task.day == cs_date)
    try:
        last_task = Task.select(Task.day).where(
            Task.user == user,
            Task.changeset.is_null(False)).order_by(Task.day.desc()).get()
        is_streak = last_task.day == cs_date - cs_date.resolution
    except Task.DoesNotExist:
        is_streak = False
    task.changeset = changeset
    task.correct = conforms
    if is_streak:
        user.streak += 1
    else:
        user.streak = 1
    user.score += int(math.log(user.streak + 1, 2))
    if conforms:
        flash('An extra point for completing the task')
        user.score += 1
    if user.level < len(config.LEVELS) + 1:
        if user.score >= config.LEVELS[user.level - 1]:
            user.level += 1
            flash('Congratulations on gaining a level!')
    with database.atomic():
        task.save()
        user.save()
    flash('Changeset noted, thank you!')
    return redirect(url_for('front'))
예제 #12
0
 def testRecurs(self):
     tui.addInputAnswers("y")
     self.cmd.do_t_add("x t1")
     task = Task.get(1)
     self.cmd.do_t_recurs("1 daily 10:00")
     desc = str(task.recurrence)
     self.cmd.do_t_recurs("1 weekly FR 23:00")
     self.cmd.do_t_recurs("1 weekly fr 23:00")
     self.cmd.do_t_recurs("1 weekly Fr 23:00")
     self.cmd.do_t_recurs("1 weekly Friday 23:00")
     self.cmd.do_t_recurs("1 monthly 3 13:00")
     self.cmd.do_t_recurs("1 monthly second friday 13:00")
     self.cmd.do_t_recurs("1 yearly 3/07 11:20")
     self.cmd.do_t_recurs("1 quarterly 14 11:20")
     self.cmd.do_t_recurs("1 quarterly first monday 23:20")
     self.assertNotEqual(desc, str(task.recurrence))
     self.assertEqual(task.status, "new")
     self.cmd.do_t_mark_done("1")
     self.assertEqual(task.status, "new")
예제 #13
0
    def testAdd(self):
        tui.addInputAnswers("y", "2", "4", "123")
        self.cmd.do_bug_add("x t1")

        tui.addInputAnswers("n")
        self.cmd.do_bug_add("notExistingProject newBug")

        tasks = list(Task.select())
        result = [x.title for x in tasks]
        expected = [u"t1"]
        self.assertEqual(result, expected)

        kwDict = Task.get(1).getKeywordDict()
        self.assertEqual(kwDict, dict(_severity=2, _likelihood=4, _bug=123))

        for bad_input in ("", # No project
                          "x", # No task name
                          "x t1"): # Existing task
            self.assertRaises(YokadiException, self.cmd.do_bug_add, bad_input)
예제 #14
0
파일: dbutils.py 프로젝트: ryanakca/yokadi
def getTaskFromId(line, parameterName="id"):
    """Returns a task given its id, or raise a YokadiException if it does not
    exist.
    @param line: taskId string
    @param parameterName: name of the parameter to mention in exception
    @return: Task instance or None if existingTask is False"""
    line = line.strip()
    if len(line) == 0:
        raise YokadiException("Missing <%s> parameter" % parameterName)

    # We do not use line.isdigit() because it returns True if line is '¹'!
    try:
        taskId = int(line)
    except ValueError:
        raise YokadiException("<%s> should be a number" % parameterName)

    try:
        return Task.get(taskId)
    except SQLObjectNotFound:
        raise YokadiException("Task %s does not exist. Use t_list to see all tasks" % taskId)
예제 #15
0
def submit_changeset(user, changeset, req=None):
    """Validates the changeset, records it and returns a series of messages."""
    lang = load_language_from_user('', user)['validation']
    try:
        changeset = parse_changeset_id(changeset)
        cs_date, conforms = validate_changeset(user, changeset, None, req)

        if not cs_date:
            raise ValidationError('wrong_date')

        last_task_day = get_last_task_day(user)
        if last_task_day and last_task_day >= cs_date:
            raise ValidationError('has_later_changeset')

        if cs_date < yesterday():
            raise ValidationError('old_changeset')
    except ValidationError as e:
        return [e.to_lang(lang)], False

    task = Task.get(Task.user == user, Task.day == cs_date)
    task.changeset = changeset
    task.correct = conforms

    if last_task_day == cs_date - cs_date.resolution:
        user.streak += 1
    else:
        user.streak = 1
    user.score += int(math.log(user.streak + 1, 2))
    msgs = [lang['changeset_noted'].format(user.streak)]
    if conforms:
        user.score += 1
        msgs.append(lang['extra_point'])
    if user.level < len(config.LEVELS) + 1:
        if user.score >= config.LEVELS[user.level - 1]:
            user.level += 1
            msgs.append(lang['gain_level'])

    with database.atomic():
        task.save()
        user.save()
    return msgs, True
예제 #16
0
    def testAdd(self):
        tui.addInputAnswers("y")
        self.cmd.do_t_add("x t1")

        tui.addInputAnswers("y", "y")
        self.cmd.do_t_add("x @kw1 @kw2=12 t2")

        tui.addInputAnswers("n")
        self.cmd.do_t_add("notExistingProject newTask")

        tasks = list(Task.select())
        result = [x.title for x in tasks]
        expected = [u"t1", u"t2"]
        self.assertEqual(result, expected)

        kwDict = Task.get(2).getKeywordDict()
        self.assertEqual(kwDict, dict(kw1=None, kw2=12))

        for bad_input in ("", # No project
                          "x", # No task name
                          "x t1"): # Existing task
            self.assertRaises(YokadiException, self.cmd.do_t_add, bad_input)
예제 #17
0
def get_task_by_name(task_name: str) -> Optional[Task]:
    return Task.get(name=task_name)
예제 #18
0
def validate_changeset(user, changeset, task_name=None, req=None):
    if not req:
        req = RequestsWrapper()
    resp = req.get('changeset/{}'.format(changeset))
    if resp.status != 200:
        raise ValidationError('api_error')
    ch = resp.data[0]
    uid = int(ch.get('uid'))
    if uid != user.uid:
        raise ValidationError('not_yours')
    date_str = ch.get('created_at')[:10]
    date = datetime.datetime.strptime(date_str, '%Y-%m-%d').date()
    if date < yesterday():
        raise ValidationError('old_changeset')

    try:
        if not task_name:
            task_obj = Task.get(Task.user == user, Task.day == date)
            task_name = task_obj.task
    except Task.DoesNotExist:
        get_or_create_task_for_user(user, date)
        return date, False
    task = load_task(task_name)
    if 'test' not in task:
        return date, True
    changes = None
    RE_NUMBER = re.compile(r'^\d+$')
    for t, tagtest in task['test'].items():
        if t == 'changeset':
            if not validate_tags(ch, tagtest):
                return date, False
        else:
            if changes is None:
                resp = req.get('changeset/{}/download'.format(changeset))
                if resp.status != 200:
                    raise ValidationError('api_error')
                changes = resp.data
                if changes.tag != 'osmChange':
                    raise ValidationError('api_strange')

            state_action = True
            actions = set()
            count = 1
            obj_type = None
            for part in t.split('_'):
                if state_action:
                    if part.startswith('create') or part.startswith('add'):
                        actions.add('create')
                    elif part.startswith('modif'):
                        actions.add('modify')
                    elif part.startswith('delete'):
                        actions.add('delete')
                    elif RE_NUMBER.match(part):
                        count = int(part)
                        state_action = False
                if actions:
                    if part.startswith('node'):
                        obj_type = 'node'
                    elif part.startswith('way'):
                        obj_type = 'way'
                    elif part.startswith('rel'):
                        obj_type = 'relation'
                    elif part.startswith('area'):
                        obj_type = 'area'
                    elif part.startswith('obj'):
                        obj_type = 'any'
                    if obj_type:
                        break
            if not obj_type:
                logging.error('Cannot parse a test header: %s', t)
                return date, True

            found_count = 0
            for xaction in changes:
                if xaction.tag not in actions:
                    continue
                for xobj in xaction:
                    if obj_type in ('node', 'way',
                                    'relation') and xobj.tag != obj_type:
                        continue
                    elif obj_type == 'area':
                        if xobj.tag == 'way':
                            if xobj.find('nd[1]').get('ref') != xobj.find(
                                    'nd[last()]').get('ref'):
                                continue
                        elif xobj.tag == 'relation':
                            xtype = xobj.find("tag[@k='type']")
                            if xtype is None or xtype.get(
                                    'v') != 'multipolygon':
                                continue
                        else:
                            continue
                    if validate_tags(xobj, tagtest):
                        found_count += 1
            if found_count < count:
                return date, False

    return date, True
예제 #19
0
        osc = changes_to_osc(changes, changeset_id)
        resp = requests.post('{0}/api/0.6/changeset/{1}/upload'.format(API_ENDPOINT, changeset_id), osc, auth=oauth)
        if resp.status_code == 200:
            task.status = 'done'
            task.error = str(changeset_id)
        else:
            # We don't want to exit before closing the changeset
            task.status = 'error'
            task.error = 'Server rejected the changeset with code {0}: {1}'.format(resp.code, resp.text)
        task.save()
    finally:
        resp = requests.put('{0}/api/0.6/changeset/{1}/close'.format(API_ENDPOINT, changeset_id), auth=oauth)


if __name__ == '__main__':
    if not lock():
        sys.exit(0)

    database.connect()
    database.create_tables([Task], safe=True)
    try:
        task = Task.get(Task.pending)
    except Task.DoesNotExist:
        # Yay, no jobs for us.
        sys.exit(0)

    try:
        process(task)
    except Exception as e:
        update_status_exit_on_error(task, 'system error', str(e))
예제 #20
0
def setup_general_config():
    '''
    Is used to populate the database if it has been reset.
    :return:
    '''

    long_string = ConfigType.get(type="long_string")
    long_string = ConfigType(
        type="long_string") if long_string is None else long_string

    string = ConfigType.get(type="string")
    string = ConfigType(type="string") if string is None else string

    number = ConfigType.get(type="number")
    number = ConfigType(type="number") if number is None else number

    config_list = [{
        "key":
        "frontend_url",
        "value":
        "localhost:3001",
        "config_type":
        string,
        "description":
        "The url to this frontend page (used for links in the emails)"
    }, {
        "key":
        "archive_base_url",
        "value":
        "localhost:3001/api/archive/download",
        "config_type":
        string,
        "description":
        "The base url to download archives from (used in emails to the board)"
    }, {
        "key":
        "document_template_url",
        "value":
        "https://www.overleaf.com/read/ddjdhxnkxttj",
        "config_type":
        string,
        "description":
        "The template overleaf document for the different reports (used in the emails)"
    }, {
        "key": "gotify_url",
        "value": "http://*****:*****@chalmers.it",
        "config_type": string,
        "description": "The email to the secretary"
    }, {
        "key": "board_email",
        "value": "*****@*****.**",
        "config_type": string,
        "description": "The email to the board"
    }, {
        "key": "group_email_domain",
        "value": "@chalmers.it",
        "config_type": string,
        "description": "The domain to send emails to"
    }, {
        "key": "from_email_address",
        "value": "*****@*****.**",
        "config_type": string,
        "description": "The email to send from"
    }, {
        "key":
        "mail_to_groups_subject",
        "value":
        "Dokument till sektionsmöte den {day}/{month}",
        "config_type":
        string,
        "description":
        """
The subject for "regular" email sendout (that goes out to all active groups that have documents to turn in for the meeting). \n
Description of the formatting values:  \n
 - {group_name} = The display name of the group \n
 - {meeting_day} = The day of month for the meeting \n
 - {meeting_month} = The month (number) of the meeting \n
 - {deadline_time} = The deadline time (hh:mm) \n
 - {deadline_date} = The deadline date (dd/mm) \n
 - {task_list} = A list of the tasks that the group should upload \n
 - {frontend_url} = The url to the website \n
 - {group_code} = Their unique code \n
 - {template_url} = The document (overleaf) template url \n
 - {secretary_email} = The email to the secretary \n
 - {board_display_name} = The display name of the board \n
 - {board_email} = The email to the board
 """
    }, {
        "key":
        "mail_to_groups_message",
        "value":
        "\nHej {group_name}!\n\nDen {meeting_day}/{meeting_month} är det dags för sektionsmöte och senast {deadline_time} den {deadline_date} behöver ni lämna in "
        "följande dokument: {task_list}\nDetta görs på sidan: {frontend_url}\nAnge koden: {group_code}\n\nMall för vissa "
        "dokument finns här: {template_url}\nGör en kopia av projektet (Menu -> Copy Project) och fyll i.\n\nOm ni har "
        "några frågor eller stöter på några problem kan kan ni kontakta mig på {secretary_email} eller hela {board_display_name} på {board_email} "
        ": ).",
        "config_type":
        long_string,
        "description":
        """
The body of the "regular" emails (the ones that are sent to all the active groups that should turn in documents for the meeting).  \n
Description of the formatting values:  \n
 - {group_name} = The display name of the group \n
 - {meeting_day} = The day of month for the meeting \n
 - {meeting_month} = The month (number) of the meeting \n
 - {deadline_time} = The deadline time (hh:mm) \n
 - {deadline_date} = The deadline date (dd/mm) \n
 - {task_list} = A list of the tasks that the group should upload \n
 - {frontend_url} = The url to the website \n
 - {group_code} = Their unique code \n
 - {template_url} = The document (overleaf) template url \n
 - {secretary_email} = The email to the secretary \n
 - {board_display_name} = The display name of the board \n
 - {board_email} = The email to the board
             """
    }, {
        "key":
        "mail_to_board_subject",
        "value":
        "Dokument för sektionsmöte {meeting_number} lp {meeting_lp}",
        "config_type":
        string,
        "description":
        """
The subject of the email that is sent to the board upon reaching the deadline.  \n
Description of the formatting values:  \n
 - {board_name} = The display name of the board \n
 - {meeting_number} = The number of the meeting (usually 0) \n
 - {meeting_lp} = The study period of the meeting \n
 - {meeting_archive_url} = A link to the archive download \n
 - {secretary_email} = The email to the secretary
             """
    }, {
        "key":
        "mail_to_board_message",
        "value":
        "\nHej {board_name}!\n\nDeadlinen för dokumentinsamling till sektionsmöte {meeting_number} i lp {meeting_lp} är nu nådd.\nFör "
        "nedladdning av dessa dokument klicka på denna länk: {meeting_archive_url}\n\nVid frågor, kontakta sekreteraren på {secretary_email}",
        "config_type":
        long_string,
        "description":
        """
The contents of the email that is sent out to the board upon reaching the deadline. \n
Description of the formatting values:  \n
 - {board_name} = The display name of the board \n
 - {meeting_number} = The number of the meeting (usually 0) \n
 - {meeting_lp} = The study period of the meeting \n
 - {meeting_archive_url} = A link to the archive download \n
 - {secretary_email} = The email to the secretary
             """
    }, {
        "key":
        "mail_for_stories_subject",
        "value":
        "Dokument för sektionsmöte {meeting_number} lp {meeting_lp}",
        "config_type":
        string,
        "description":
        """ 
The subject of the email that is sent to the "story groups" (i.e. the groups that needs to turn in eberattelser / vberattelser. \n
Description of the formatting values:  \n
 - {group_name_year} = Display name of the group. \n
 - {meeting_day} = The day of month that the meeting will take place \n
 - {meeting_month} = The month (number) of the meeting \n
 - {deadline_time} = The deadline time \n
 - {deadline_date} = The deadline date \n
 - {task_list} = A list of the tasks that the group will have to turn in. \n
 - {frontend_url} = A url to the frontend (upload page) \n
 - {group_code} = Their unique code \n
 - {template_url} = A link the overleaf template for the documents. \n
 - {secretary_email} = The email to the secretary \n
 - {board_display_name} = The display name of the board \n
 - {board_email} = The email to the board \n
 - {meeting_number} = The number of the meeting that study period (usually 0) \n
 - {meeting_lp} = The study period
             """
    }, {
        "key":
        "mail_for_stories",
        "value":
        "\nHej {group_name_year}!\n\nDen {meeting_day}/{meeting_month} är det dags för sektionsmöte och senast {deadline_time} den {deadline_date} behöver ni lämna in "
        "följande dokument: {task_list}\nDetta görs på sidan: {frontend_url}\nAnge koden: {group_code}\n\nMall för vissa "
        "dokument finns här: {template_url}\nGör en kopia av projektet (Menu -> Copy Project) och fyll i.\n "
        "Kontakta revisorerna på [email protected] för mer information om vad som behövs göras innan ni "
        "kan bli rekomenderade att bli ansvarsbefriade.\n\nOm ni har "
        "några frågor eller stöter på några problem kan kan ni kontakta mig på {secretary_email} eller hela {board_display_name} på {board_email} "
        ": ).",
        "config_type":
        long_string,
        "description":
        """
The body of the email that is sent to the "story groups" (i.e. the groups that needs to turn in eberattelser / vberattelser) \n
Description of the formatting values:  \n
 - {group_name_year} = Display name of the group. \n
 - {meeting_day} = The day of month that the meeting will take place \n
 - {meeting_month} = The month (number) of the meeting \n
 - {deadline_time} = The deadline time \n
 - {deadline_date} = The deadline date \n
 - {task_list} = A list of the tasks that the group will have to turn in. \n
 - {frontend_url} = A url to the frontend (upload page) \n
 - {group_code} = Their unique code \n
 - {template_url} = A link the overleaf template for the documents. \n
 - {secretary_email} = The email to the secretary \n
 - {board_display_name} = The display name of the board \n
 - {board_email} = The email to the board \n
 - {meeting_number} = The number of the meeting that study period (usually 0) \n
 - {meeting_lp} = The study period
             """
    }, {
        "key": "board_display_name",
        "value": "styrIT",
        "config_type": string,
        "description": "The display name of the board"
    }, {
        "key":
        "minutes_after_deadline_to_mail",
        "value":
        "5",
        "config_type":
        number,
        "description":
        "The amount of minutes to wait extra after the deadline before sending the email to the board"
    }, {
        "key":
        "check_for_deadline_frequency",
        "value":
        "5",
        "config_type":
        number,
        "description":
        "The frequence (in minutes) to check if any deadlines have been reached"
    }, {
        "key":
        "possible_years_back_for_stories",
        "value":
        "5",
        "config_type":
        number,
        "description":
        "The number of years back that one should be able to select story groups for (usually 5 due to tax reasons)"
    }]

    for config in config_list:
        conf = Config.get(key=config["key"])
        if conf is None:
            Config(key=config["key"],
                   value=config["value"],
                   config_type=config["config_type"],
                   description=config["description"])
        else:
            # Since the only way to change the description is here,
            # we always want the db version to be up to date with this list on application restart.
            conf.description = config["description"]

    # Setup groups and tasks
    groups = [{
        "codeName": "armit",
        "displayName": "ArmIT"
    }, {
        "codeName": "digit",
        "displayName": "digIT"
    }, {
        "codeName": "fanbarerit",
        "displayName": "FanbärerIT"
    }, {
        "codeName": "fritid",
        "displayName": "frITid"
    }, {
        "codeName": "mrcit",
        "displayName": "MRCIT"
    }, {
        "codeName": "nollkit",
        "displayName": "NollKIT"
    }, {
        "codeName": "prit",
        "displayName": "P.R.I.T."
    }, {
        "codeName": "sexit",
        "displayName": "sexIT"
    }, {
        "codeName": "snit",
        "displayName": "snIT"
    }, {
        "codeName": "styrit",
        "displayName": "styrIT"
    }, {
        "codeName": "equalit",
        "displayName": "EqualIT"
    }, {
        "codeName": "flashit",
        "displayName": "FlashIT"
    }]

    tasks = [{
        "codeName": "vplan",
        "displayName": "Verksamhetsplan / Operational plan"
    }, {
        "codeName": "budget",
        "displayName": "Budget"
    }, {
        "codeName": "vrapport",
        "displayName": "Verksamhetsrapport / Operational report"
    }, {
        "codeName": "vberattelse",
        "displayName": "Verksamhetsberättelse / Operational story"
    }, {
        "codeName": "eberattelse",
        "displayName": "Ekonomisk Berättelse / Economic story"
    }]

    for group in groups:
        if Group.get(name=group["codeName"]) is None:
            new_group = Group(name=group["codeName"],
                              display_name=group["displayName"])

            if GroupYear.get(group=new_group, year="active") is None:
                GroupYear(group=new_group, year="active", finished=False)

    for task in tasks:
        if Task.get(name=task["codeName"]) is None:
            Task(name=task["codeName"], display_name=task["displayName"])

    commit()
    print("Finished loading database data from general config file.")