예제 #1
0
    def write(self, calendar_data: CalendarData, data: Dict) -> bool:
        current_day, current_month, current_year = GregorianCalendar.current_date(
        )

        output = self._get_header(calendar_name=data["name"])  # type: str

        year = current_year
        month = current_month

        for index in range(self.months_to_export):
            if month < 12:
                month += 1
            else:
                month = 1
                year += 1

            all_tasks = calendar_data.tasks_from_calendar(year=year,
                                                          month=month,
                                                          data=data)
            all_tasks = calendar_data.add_repetitive_tasks_from_calendar(
                year=year,
                month=month,
                data=data,
                tasks=all_tasks,
                view_past_tasks=False)
            for day, tasks in all_tasks.items():
                for task in tasks:
                    output += self._get_task(task, year, month, day)

        output += self._get_footer()

        print(output)
        return True
예제 #2
0
def main_calendar_action(calendar_id: str) -> Response:
    current_day, current_month, current_year = GregorianCalendar.current_date()
    year = int(request.args.get("y", current_year))
    year = max(min(year, config.MAX_YEAR), config.MIN_YEAR)
    month = int(request.args.get("m", current_month))
    month = max(min(month, 12), 1)
    month_name = GregorianCalendar.MONTH_NAMES[month - 1]

    view_past_tasks = request.cookies.get("ViewPastTasks", "1") == "1"

    calendar_data = CalendarData(config.DATA_FOLTER)
    try:
        data = calendar_data.load_calendar(calendar_id)
    except FileNotFoundError:
        abort(404)

    tasks = calendar_data.tasks_from_calendar(year=year, month=month, view_past_tasks=view_past_tasks, data=data)
    tasks = calendar_data.add_repetitive_tasks_from_calendar(year=year, month=month, data=data, tasks=tasks,
                                                             view_past_tasks=view_past_tasks)

    return render_template("calendar.html",
                           calendar_id=calendar_id,
                           year=year,
                           month=month,
                           month_name=month_name,
                           current_year=current_year,
                           current_month=current_month,
                           current_day=current_day,
                           month_days=GregorianCalendar.month_days(year=year, month=month),
                           previous_month_link=previous_month_link(year=year, month=month),
                           next_month_link=next_month_link(year=year, month=month),
                           base_url=config.BASE_URL,
                           tasks=tasks)
예제 #3
0
def test_no_data_nor_calendar_id_supplied_to_retrieve_tasks(
        calendar_data: CalendarData) -> None:
    with pytest.raises(ValueError):
        calendar_data.tasks_from_calendar(year=2001,
                                          month=1,
                                          data=None,
                                          calendar_id=None)
예제 #4
0
def edit_task_action(calendar_id: str, year: int, month: int, day: int, task_id: int) -> Response:
    month_names = GregorianCalendar.MONTH_NAMES
    calendar_data = CalendarData(config.DATA_FOLTER)

    repeats = request.args.get("repeats") == "1"
    try:
        if repeats:
            task = calendar_data.repetitive_task_from_calendar(calendar_id=calendar_id, year=year, month=month,
                                                               task_id=int(task_id))
        else:
            task = calendar_data.task_from_calendar(calendar_id=calendar_id, year=year, month=month, day=day,
                                                    task_id=int(task_id))
    except (FileNotFoundError, IndexError):
        abort(404)

    if task["details"] == "&nbsp;":
        task["details"] = ""
    else:
        task["details"] = task["details"].replace("<br>", "\n")

    return render_template("task.html",
                           calendar_id=calendar_id,
                           year=year,
                           month=month,
                           day=day,
                           min_year=config.MIN_YEAR,
                           max_year=config.MAX_YEAR,
                           month_names=month_names,
                           task=task,
                           base_url=config.BASE_URL,
                           editing=True)
예제 #5
0
def test_non_existing_individual_task_retrieval(
        calendar_data: CalendarData) -> None:
    with pytest.raises(ValueError):
        calendar_data.task_from_calendar(calendar_id="sample_data_file",
                                         year=2017,
                                         month=11,
                                         day=6,
                                         task_id=0)
예제 #6
0
def test_loads_normal_tasks_from_calendar_given_data(
        calendar_data: CalendarData) -> None:
    data = calendar_data.load_calendar("sample_data_file")
    tasks = calendar_data.tasks_from_calendar(year=2017, month=12, data=data)
    assert len(tasks) == 1
    assert len(tasks["25"]) == 2
    assert tasks["25"][0]["id"] in [0, 1]
    assert tasks["25"][1]["id"] in [0, 1]
    assert tasks["25"][0]["id"] != tasks["25"][1]["id"]
예제 #7
0
def delete_task_action(calendar_id: str, year: str, month: str, day: str, task_id: str) -> Response:
    calendar_data = CalendarData(config.DATA_FOLTER)
    calendar_data.delete_task(calendar_id=calendar_id,
                              year_str=year,
                              month_str=month,
                              day_str=day,
                              task_id=int(task_id))
    export_to_icalendar(calendar_data, calendar_id)

    return jsonify({})
예제 #8
0
def hide_repetition_task_instance_action(calendar_id: str, year: str, month: str, day: str, task_id: str) -> Response:
    calendar_data = CalendarData(config.DATA_FOLTER)
    calendar_data.hide_repetition_task_instance(calendar_id=calendar_id,
                                                year_str=year,
                                                month_str=month,
                                                day_str=day,
                                                task_id_str=task_id)
    export_to_icalendar(calendar_data, calendar_id)

    return jsonify({})
예제 #9
0
def update_task_day_action(calendar_id: str, year: str, month: str, day: str, task_id: str) -> Response:
    new_day = request.data.decode("utf-8")

    calendar_data = CalendarData(config.DATA_FOLTER)
    calendar_data.update_task_day(calendar_id=calendar_id,
                                  year_str=year,
                                  month_str=month,
                                  day_str=day,
                                  task_id=int(task_id),
                                  new_day_str=new_day)
    export_to_icalendar(calendar_data, calendar_id)

    return jsonify({})
예제 #10
0
def test_if_dont_want_to_view_past_tasks_dont_appear(
        calendar_data: CalendarData) -> None:
    all_tasks = calendar_data.tasks_from_calendar(
        year=2001,
        month=1,
        view_past_tasks=True,
        calendar_id="past_normal_tasks")
    non_past_tasks = calendar_data.tasks_from_calendar(
        year=2001,
        month=2,
        view_past_tasks=False,
        calendar_id="past_normal_tasks")

    assert len(all_tasks) > len(non_past_tasks)
    assert len(non_past_tasks) == 0
예제 #11
0
def test_hidden_montly_weekday_repetitions_dont_appear(
        calendar_data: CalendarData) -> None:
    year = 2017
    month = 12
    data = calendar_data.load_calendar(
        "repetitive_monthly_weekday_hidden_task_data_file")
    tasks = calendar_data.tasks_from_calendar(year=year,
                                              month=month,
                                              data=data)

    tasks = calendar_data.add_repetitive_tasks_from_calendar(year=year,
                                                             month=month,
                                                             data=data,
                                                             tasks=tasks)
    assert len(tasks) == 0
예제 #12
0
def new_task(calendar_id: str, year: int, month: int) -> Response:
    current_day, current_month, current_year = GregorianCalendar.current_date()
    year = max(min(int(year), config.MAX_YEAR), config.MIN_YEAR)
    month = max(min(int(month), 12), 1)
    month_names = GregorianCalendar.MONTH_NAMES

    if current_month == month and current_year == year:
        day = current_day
    else:
        day = 1

    task = {
        "date": CalendarData.date_for_frontend(year=year, month=month,
                                               day=day),
        "is_all_day": True,
        "repeats": False,
        "details": ""
    }

    return render_template("task.html",
                           calendar_id=calendar_id,
                           year=year,
                           month=month,
                           min_year=config.MIN_YEAR,
                           max_year=config.MAX_YEAR,
                           month_names=month_names,
                           task=task,
                           base_url=config.BASE_URL,
                           editing=False)
예제 #13
0
def test_existing_repetitive_task_retrieval(
        calendar_data: CalendarData) -> None:
    task_id = 2
    task = calendar_data.repetitive_task_from_calendar(
        calendar_id="sample_data_file", year=2017, month=11, task_id=task_id)
    assert task is not None
    assert task["id"] == task_id
    assert task["is_all_day"] is True
예제 #14
0
def delete_task(calendar_id: str, year: str, month: str, day: str,
                task_id: str) -> Response:
    CalendarData(config.DATA_FOLTER).delete_task(calendar_id=calendar_id,
                                                 year_str=year,
                                                 month_str=month,
                                                 day_str=day,
                                                 task_id=int(task_id))
    return jsonify({})
예제 #15
0
def hide_repetition_task_instance(calendar_id: str, year: str, month: str,
                                  day: str, task_id: str) -> Response:
    CalendarData(config.DATA_FOLTER).hide_repetition_task_instance(
        calendar_id=calendar_id,
        year_str=year,
        month_str=month,
        day_str=day,
        task_id_str=task_id)
    return jsonify({})
예제 #16
0
 def wrapper(*args: Any, **kwargs: Any) -> Any:
     username = get_session_username(str(request.cookies.get(SESSION_ID)))
     authorization = Authorization(calendar_data=CalendarData(data_folder=config.DATA_FOLTER))
     if "calendar_id" not in kwargs:
         raise ValueError("calendar_id")
     calendar_id = str(kwargs["calendar_id"])
     if not authorization.can_access(username=username, calendar_id=calendar_id):
         abort(403)
     return decorated_function(*args, **kwargs)
예제 #17
0
def save_task_action(calendar_id: str) -> Response:
    title = request.form["title"].strip()
    date = request.form.get("date", "")
    if len(date) > 0:
        date_fragments = re.split("-", date)
        year = int(date_fragments[0])  # type: Optional[int]
        month = int(date_fragments[1])  # type: Optional[int]
        day = int(date_fragments[2])  # type: Optional[int]
    else:
        year = month = day = None
    is_all_day = request.form.get("is_all_day", "0") == "1"
    due_time = request.form["due_time"]
    details = request.form["details"].replace("\r", "").replace("\n", "<br>")
    color = request.form["color"]
    
    
    username = get_session_username(session_id=str(request.cookies.get(SESSION_ID)))
    user_data = authentication.user_data(username=username)
    color = user_data['color']
    has_repetition = request.form.get("repeats", "0") == "1"
    repetition_type = request.form.get("repetition_type")
    repetition_subtype = request.form.get("repetition_subtype")
    repetition_value = int(request.form["repetition_value"])

    calendar_data = CalendarData(config.DATA_FOLTER)
    calendar_data.create_task(calendar_id=calendar_id,
                              year=year,
                              month=month,
                              day=day,
                              title=title,
                              is_all_day=is_all_day,
                              due_time=due_time,
                              details=details,
                              color=color,
                              has_repetition=has_repetition,
                              repetition_type=repetition_type,
                              repetition_subtype=repetition_subtype,
                              repetition_value=repetition_value)
    export_to_icalendar(calendar_data, calendar_id)

    if year is None:
        return redirect("{}/{}/".format(config.BASE_URL, calendar_id), code=302)
    else:
        return redirect("{}/{}/?y={}&m={}".format(config.BASE_URL, calendar_id, year, month), code=302)
예제 #18
0
def jsonRest(calendar_id: str) -> Response:
    current_day, current_month, current_year = GregorianCalendar.current_date()
    year = int(request.args.get("y", current_year))
    year = max(min(year, config.MAX_YEAR), config.MIN_YEAR)
    month = int(request.args.get("m", current_month))
    month = max(min(month, 12), 1)
    month_name = GregorianCalendar.MONTH_NAMES[month - 1]

    view_past_tasks = request.cookies.get("ViewPastTasks", "1") == "1"

    calendar_data = CalendarData(config.DATA_FOLTER)
    try:
        data = calendar_data.load_calendar(calendar_id)
    except FileNotFoundError:
        abort(404)

    tasks = calendar_data.tasks_from_calendar(year=year,
                                              month=month,
                                              view_past_tasks=view_past_tasks,
                                              data=data)
    tasks = calendar_data.add_repetitive_tasks_from_calendar(
        year=year,
        month=month,
        data=data,
        tasks=tasks,
        view_past_tasks=view_past_tasks)

    merged = {}
    merged[year] = {}
    merged[year][month] = tasks

    nextMonth = month
    nextYear = year
    if (month == 12):
        nextMonth = 1
        nextYear = year + 1
    else:
        nextMonth = month + 1

    tasksNextMonth = calendar_data.tasks_from_calendar(
        year=nextYear,
        month=nextMonth,
        view_past_tasks=view_past_tasks,
        data=data)
    tasksNextMonth = calendar_data.add_repetitive_tasks_from_calendar(
        year=nextYear,
        month=nextMonth,
        data=data,
        tasks=tasksNextMonth,
        view_past_tasks=view_past_tasks)

    if (year == nextYear):
        merged[year][nextMonth] = tasksNextMonth
    else:
        merged[nextYear] = {}
        merged[nextYear][nextMonth] = tasksNextMonth

    return json.dumps(merged)
예제 #19
0
def test_existing_individual_task_retrieval(
        calendar_data: CalendarData) -> None:
    task_id = 4
    task = calendar_data.task_from_calendar(calendar_id="sample_data_file",
                                            year=2017,
                                            month=11,
                                            day=6,
                                            task_id=4)
    assert task is not None
    assert task["id"] == task_id
    assert task["is_all_day"] is True
예제 #20
0
def test_errors_if_data_missing_keys(calendar_data: CalendarData) -> None:
    with pytest.raises(ValueError):
        calendar_data.tasks_from_calendar(year=2001,
                                          month=1,
                                          calendar_id=None,
                                          data={})
    with pytest.raises(ValueError):
        calendar_data.tasks_from_calendar(year=2001,
                                          month=1,
                                          calendar_id=None,
                                          data={"tasks": {}})
    with pytest.raises(ValueError):
        calendar_data.tasks_from_calendar(
            year=2001,
            month=1,
            calendar_id=None,
            data={"tasks": {
                "normal": {},
                "hidden_repetition": {}
            }})
    with pytest.raises(ValueError):
        calendar_data.tasks_from_calendar(
            year=2001,
            month=1,
            calendar_id=None,
            data={"tasks": {
                "normal": {},
                "repetition": {}
            }})
    with pytest.raises(ValueError):
        calendar_data.tasks_from_calendar(
            year=2001,
            month=1,
            calendar_id=None,
            data={"tasks": {
                "repetition": {},
                "hidden_repetition": {}
            }})
예제 #21
0
def test_joins_repetitive_tasks_with_normal_ones(
        calendar_data: CalendarData) -> None:
    year = 2017
    month = 11
    data = calendar_data.load_calendar("sample_data_file")

    tasks = calendar_data.tasks_from_calendar(year=year,
                                              month=month,
                                              data=data)
    assert len(tasks) == 1

    tasks = calendar_data.add_repetitive_tasks_from_calendar(year=year,
                                                             month=month,
                                                             data=data,
                                                             tasks=tasks)
    assert len(tasks) > 0
    repetitive_weekly_weekday_task_ocurrences = 4  # month has 4 mondays
    repetitive_weekly_weekday_3_task_ocurrences = 5  # month has 5 thursdays
    repetitive_monthly_weekday_task_ocurrences = 1
    repetitive_monthly_monthday_task_ocurrences = 1

    # We"re counting the number of days with tasks, not the exact number of tasks (day 6 has 2 tasks)
    assert len(tasks) == (repetitive_weekly_weekday_task_ocurrences +
                          repetitive_monthly_weekday_task_ocurrences +
                          repetitive_monthly_monthday_task_ocurrences +
                          repetitive_weekly_weekday_3_task_ocurrences)
    assert len(tasks["6"]) == 2

    # Normal task should be first (as repetitive ones are appended afterwards)
    assert tasks["6"][0]["id"] == 4
    assert "repetition_value" not in tasks["6"][0]
    assert "repetition_value" in tasks["6"][1]
    assert tasks["6"][1]["repetition_value"] == 0
    assert tasks["6"][1][
        "repetition_subtype"] == CalendarData.REPETITION_SUBTYPE_WEEK_DAY
    assert tasks["6"][1][
        "repetition_type"] == CalendarData.REPETITION_TYPE_WEEKLY
예제 #22
0
def test_loads_a_valid_data_file(calendar_data: CalendarData) -> None:
    calendar = calendar_data.load_calendar("sample_data_file")
    assert calendar is not None
    assert type(calendar) == dict
    assert "tasks" in calendar
    assert "repetition" in calendar["tasks"]
    assert len(calendar["tasks"]["repetition"]) > 0
    assert "normal" in calendar["tasks"]
    assert "2017" in calendar["tasks"]["normal"]
    assert "12" in calendar["tasks"]["normal"]["2017"]
    assert "25" in calendar["tasks"]["normal"]["2017"]["12"]
    assert len(calendar["tasks"]["normal"]["2017"]["12"]["25"]) == 2
    task = calendar["tasks"]["normal"]["2017"]["12"]["25"][1]
    assert task["id"] == 1
    assert task["is_all_day"] is False
예제 #23
0
def test_creates_new_normal_task(save_calendar_mock: MagicMock,
                                 calendar_data: CalendarData) -> None:
    year = 2017
    month = 12
    day = 10
    title = "an irrelevant title"
    is_all_day = True
    due_time = "00:00"
    details = ""
    color = "an_irrelevant_color"
    has_repetition = False
    repetition_type = ""
    repetition_subtype = ""
    repetition_value = 0
    calendar_id = "sample_empty_data_file"

    result = calendar_data.create_task(calendar_id=calendar_id,
                                       year=year,
                                       month=month,
                                       day=day,
                                       title=title,
                                       is_all_day=is_all_day,
                                       due_time=due_time,
                                       details=details,
                                       color=color,
                                       has_repetition=has_repetition,
                                       repetition_type=repetition_type,
                                       repetition_subtype=repetition_subtype,
                                       repetition_value=repetition_value)
    assert result is True

    save_calendar_mock.assert_called_once_with(contents=ANY,
                                               filename=calendar_id)
    args, kwargs = save_calendar_mock.call_args
    assert "contents" in kwargs
    assert "tasks" in kwargs["contents"]
    assert "normal" in kwargs["contents"]["tasks"]
    assert str(year) in kwargs["contents"]["tasks"]["normal"]
    assert str(month) in kwargs["contents"]["tasks"]["normal"][str(year)]
    assert str(day) in kwargs["contents"]["tasks"]["normal"][str(year)][str(
        month)]
    assert len(kwargs["contents"]["tasks"]["normal"][str(year)][str(month)][
        str(day)]) == 1
    assert "title" in kwargs["contents"]["tasks"]["normal"][str(year)][str(
        month)][str(day)][0]
    assert kwargs["contents"]["tasks"]["normal"][str(year)][str(month)][str(
        day)][0]["title"] == title
예제 #24
0
def update_task_action(calendar_id: str, year: str, month: str, day: str,
                       task_id: str) -> Response:
    # Logic is same as save + delete, could refactor but can wait until need to change any save/delete logic

    calendar_data = CalendarData(config.DATA_FOLTER)

    # For creation of "updated" task use only form data
    title = request.form["title"].strip()
    date = request.form.get("date", "")
    if len(date) > 0:
        fragments = re.split("-", date)
        updated_year = int(fragments[0])  # type: Optional[int]
        updated_month = int(fragments[1])  # type: Optional[int]
        updated_day = int(fragments[2])  # type: Optional[int]
    else:
        updated_year = updated_month = updated_day = None
    is_all_day = request.form.get("is_all_day", "0") == "1"
    due_time = request.form["due_time"]
    details = request.form["details"].replace("\r", "").replace("\n", "<br>")
    color = request.form["color"]
    has_repetition = request.form.get("repeats", "0") == "1"
    repetition_type = request.form.get("repetition_type", "")
    repetition_subtype = request.form.get("repetition_subtype", "")
    repetition_value = int(request.form["repetition_value"])  # type: int

    calendar_data.create_task(calendar_id=calendar_id,
                              year=updated_year,
                              month=updated_month,
                              day=updated_day,
                              title=title,
                              is_all_day=is_all_day,
                              due_time=due_time,
                              details=details,
                              color=color,
                              has_repetition=has_repetition,
                              repetition_type=repetition_type,
                              repetition_subtype=repetition_subtype,
                              repetition_value=repetition_value)
    # For deletion of old task data use only url data
    calendar_data.delete_task(calendar_id=calendar_id,
                              year_str=year,
                              month_str=month,
                              day_str=day,
                              task_id=int(task_id))
    export_to_icalendar(calendar_data, calendar_id)

    if updated_year is None:
        return redirect("{}/{}/".format(config.BASE_URL, calendar_id),
                        code=302)
    else:
        return redirect("{}/{}/?y={}&m={}".format(config.BASE_URL, calendar_id,
                                                  updated_year, updated_month),
                        code=302)
예제 #25
0
def export_to_icalendar(calendar_data: CalendarData, calendar_id: str) -> bool:
    if not config.FEATURE_FLAG_ICAL_EXPORT:
        return True

    session_id = str(request.cookies.get(SESSION_ID))
    username = get_session_username(session_id)
    data = calendar_data.load_calendar(calendar_id)

    # TODO: set output folder from config, etc. and write the data. helper for filename format?
    # detect if ics_key key not present and throw exception to force configuration.

    # authentication = Authentication(data_folder=config.USERS_DATA_FOLDER, password_salt=config.PASSWORD_SALT)
    # authentication.user_data(username=username)["ics_key"]

    ical_exporter: ICalendar = ICalendar(
        username=username,
        timezone=config.TIMEZONE,
        months_to_export=config.MONTHS_TO_EXPORT)
    ical_exporter.write(calendar_data=calendar_data, data=data)
    return True
예제 #26
0
def save_task(calendar_id: str) -> Response:
    title = request.form["title"]
    date = request.form.get("date", "")
    if len(date) > 0:
        date_fragments = re.split('-', date)
        year = int(date_fragments[0])  # type: Optional[int]
        month = int(date_fragments[1])  # type: Optional[int]
        day = int(date_fragments[2])  # type: Optional[int]
    else:
        year = month = day = None
    is_all_day = request.form.get("is_all_day", "0") == "1"
    due_time = request.form["due_time"]
    details = request.form["details"].replace("\r", "").replace("\n", "<br>")
    color = request.form["color"]
    has_repetition = request.form.get("repeats", "0") == "1"
    repetition_type = request.form.get("repetition_type")
    repetition_subtype = request.form.get("repetition_subtype")
    repetition_value = int(request.form["repetition_value"])

    CalendarData(config.DATA_FOLTER).create_task(
        calendar_id=calendar_id,
        year=year,
        month=month,
        day=day,
        title=title,
        is_all_day=is_all_day,
        due_time=due_time,
        details=details,
        color=color,
        has_repetition=has_repetition,
        repetition_type=repetition_type,
        repetition_subtype=repetition_subtype,
        repetition_value=repetition_value)

    if year is None:
        return redirect("{}/{}/".format(config.BASE_URL, calendar_id),
                        code=302)
    else:
        return redirect("{}/{}/?y={}&m={}".format(config.BASE_URL, calendar_id,
                                                  year, month),
                        code=302)
예제 #27
0
def authorization() -> Authorization:
    return Authorization(calendar_data=CalendarData("test/fixtures"))
예제 #28
0
def test_non_existing_repetitive_task_retrieval(
        calendar_data: CalendarData) -> None:
    with pytest.raises(IndexError):
        calendar_data.repetitive_task_from_calendar(
            calendar_id="sample_data_file", year=2017, month=11, task_id=111)
예제 #29
0
def calendar_data() -> CalendarData:
    return CalendarData("test/fixtures")