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
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)
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)
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"] == " ": 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)
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)
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"]
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({})
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({})
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({})
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
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
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)
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
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({})
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({})
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)
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)
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)
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
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": {} }})
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
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
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
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)
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
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)
def authorization() -> Authorization: return Authorization(calendar_data=CalendarData("test/fixtures"))
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)
def calendar_data() -> CalendarData: return CalendarData("test/fixtures")