Skip to content

Commit

Permalink
fix: The date in the downloaded Plain text file is not the event date…
Browse files Browse the repository at this point in the history
… [CPCN-388] (#687)

* fix: The date in the downloaded Plain text file is not the event date [CPCN-388]

* include timezone and format date time based on timezone

* refactore code

* add test and reformat code via black

* add types and refactore code

* fix black

* add missing return statement

* update logic and return type

* address comment

* refactore code

* fix tests

* remove unwanted date parsing and refactored code

* use configured datetime for formatting

* refactore event formatting for better readability

* minor change

* update default format

* update datetime string output
  • Loading branch information
devketanpro authored Dec 22, 2023
1 parent c96f108 commit 431a1df
Show file tree
Hide file tree
Showing 5 changed files with 222 additions and 3 deletions.
9 changes: 9 additions & 0 deletions newsroom/gettext.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,15 @@ def set_session_timezone(timezone: str):
current_app.session_timezone = timezone


def clear_session_timezone():
try:
session.pop("timezone", None)
except RuntimeError:
pass

current_app.config.pop("SESSION_TIMEZONE", None)


def setup_babel(app):
babel = Babel(app)

Expand Down
109 changes: 108 additions & 1 deletion newsroom/template_filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,14 @@

from flask import current_app as app
from eve.utils import str_to_date
from flask_babel import format_time, format_date, format_datetime, get_locale
from flask_babel import format_time, format_date, format_datetime, get_locale, lazy_gettext
from flask_babel.speaklater import LazyString
from jinja2.utils import htmlsafe_json_dumps # type: ignore
from superdesk.text_utils import get_text, get_word_count, get_char_count
from superdesk.utc import utcnow
from datetime import datetime
from newsroom.gettext import set_session_timezone, get_session_timezone, clear_session_timezone
from enum import Enum

from newsroom.user_roles import UserRole

Expand All @@ -22,6 +24,13 @@
_hash_cache = {}


class ScheduleType(Enum):
ALL_DAY = "ALL_DAY"
MULTI_DAY = "MULTI_DAY"
NO_DURATION = "NO_DURATION"
REGULAR = "REGULAR"


def get_client_format(key) -> Optional[str]:
locale = str(get_locale())
try:
Expand Down Expand Up @@ -61,6 +70,104 @@ def parse_date(datetime):
return datetime


def get_schedule_type(start: datetime, end: datetime, all_day: bool, no_end_time: bool) -> ScheduleType:
"""
Determine the schedule type based on event start and end times.
params :
- start (datetime): Start time of the event.
- end (datetime): End time of the event.
- all_day (bool): True if the event is an all-day event.
- no_end_time (bool): True if the event has no specified end time.
return:
ScheduleType: The type of schedule (ALL_DAY, NO_DURATION, MULTI_DAY, REGULAR).
"""
duration = int((end - start).total_seconds() / 60) if not no_end_time and end else 0
DAY_IN_MINUTES = 24 * 60 - 1

if all_day:
return ScheduleType.ALL_DAY if duration in (0, DAY_IN_MINUTES) else ScheduleType.MULTI_DAY

if no_end_time:
return ScheduleType.NO_DURATION

if duration >= DAY_IN_MINUTES and start.date() != (end.date() if end else None):
return ScheduleType.MULTI_DAY

if duration <= DAY_IN_MINUTES and start.date() == (end.date() if end else None):
return ScheduleType.ALL_DAY

if duration == 0 and start.time() == (end.time() if end else None):
return ScheduleType.NO_DURATION

return ScheduleType.REGULAR


def is_item_tbc(item: dict) -> bool:
event_tbc = item.get("event", {}).get("_time_to_be_confirmed", False)
planning = item.get("planning_items", [])
return event_tbc or (planning and planning[0].get("_time_to_be_confirmed", False))


def format_event_datetime(item: dict) -> str:
date_info = item.get("dates", {})

if not date_info:
return ""

tz = date_info.get("tz", get_session_timezone())
try:
# Set the session timezone
set_session_timezone(tz)

start = parse_date(date_info.get("start"))
end = parse_date(date_info.get("end")) if date_info.get("end") else None
all_day = date_info.get("all_day")
no_end_time = date_info.get("no_end_time")
is_tbc_item = is_item_tbc(item)

schedule_type = get_schedule_type(start, end, all_day, no_end_time)

formatted_start = datetime_long(start)
formatted_end = datetime_long(end) if end else None

if is_tbc_item:
if schedule_type == ScheduleType.MULTI_DAY:
return lazy_gettext("Date: {start} to {end} ({tz}) (Time to be confirmed)").format(
start=formatted_start, end=formatted_end, tz=tz
)

else:
return lazy_gettext("Date: {start} ({tz}) (Time to be confirmed)").format(start=formatted_start, tz=tz)

elif schedule_type in (ScheduleType.ALL_DAY, ScheduleType.NO_DURATION):
return lazy_gettext("Date: {start} ({tz})").format(start=formatted_start, tz=tz)

elif schedule_type == ScheduleType.MULTI_DAY:
return lazy_gettext("Date: {start} to {end} ({tz})").format(start=formatted_start, end=formatted_end, tz=tz)
elif schedule_type == ScheduleType.REGULAR:
return lazy_gettext("Time: {start_time} to {end_time} on Date: {date} ({tz})").format(
start_time=notification_time(start),
end_time=notification_time(end),
date=notification_date(start),
tz=tz,
)
else:
# Default
return lazy_gettext("Date: {start_time} {start_date} to {end_time} {end_date} ({tz})").format(
start_time=notification_time(start),
start_date=notification_date(start),
end_time=notification_time(end),
end_date=notification_date(end),
tz=tz,
)

finally:
# clear session timezone
clear_session_timezone()


def datetime_short(datetime):
if datetime:
return format_datetime(parse_date(datetime), app.config["DATETIME_FORMAT_SHORT"])
Expand Down
2 changes: 1 addition & 1 deletion newsroom/templates/download_agenda.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
{% endif %}
{%- if item.definition_long %}Definition: {{ item.definition_long }}
{% endif -%}
On: {{ item.dates.start | datetime_long }}
{{ item | format_event_datetime }}

{%- if item.subject %}Category: {{ item.subject|join(', ', attribute='name')}}
{% endif %}
Expand Down
2 changes: 2 additions & 0 deletions newsroom/web/factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
initials,
get_highlighted_field,
get_item_category_names,
format_event_datetime,
)
from newsroom.template_loaders import LocaleTemplateLoader
from newsroom.notifications.notifications import get_initial_notifications
Expand Down Expand Up @@ -141,6 +142,7 @@ def _setup_jinja(self):
self.add_template_filter(get_location_string, "location_string")
self.add_template_filter(get_agenda_dates, "agenda_dates_string")
self.add_template_filter(get_item_category_names, "category_names")
self.add_template_filter(format_event_datetime)

self.jinja_loader = LocaleTemplateLoader(self._theme_folders)

Expand Down
103 changes: 102 additions & 1 deletion tests/core/test_template_filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

from datetime import datetime
from flask_babel import lazy_gettext
from newsroom.template_filters import datetime_long, parse_date
from newsroom.template_filters import datetime_long, parse_date, format_event_datetime
from newsroom.template_loaders import set_template_locale
import newsroom.template_filters as template_filters

Expand Down Expand Up @@ -50,3 +50,104 @@ def test_notification_date_time_filters():
assert "11:00" == template_filters.notification_time(d)
assert "octobre 31, 2023" == template_filters.notification_date(d)
assert "11:00 octobre 31, 2023" == template_filters.notification_datetime(d)


def test_format_event_datetime():
# Case 1: Regular event with specific start and end times
event1 = {
"dates": {
"tz": "Asia/Calcutta",
"start": "2023-10-31T18:30:00+0000",
"end": "2023-11-01T20:45:00+0000",
"all_day": False,
"no_end_time": False,
},
}
assert "Date: 01/11/2023 00:00 to 02/11/2023 02:15 (Asia/Calcutta)" == format_event_datetime(event1)

# Case 2: All-day event
event2 = {
"dates": {
"tz": "Asia/Calcutta",
"end": "2023-12-18T18:29:59+0000",
"start": "2023-12-17T18:30:00+0000",
"all_day": True,
"no_end_time": False,
},
}
assert "Date: 18/12/2023 00:00 (Asia/Calcutta)" == format_event_datetime(event2)

# Case 3: Time-to-be-confirmed event
event3 = {
"dates": {
"tz": "Asia/Calcutta",
"start": "2023-10-31T18:30:00+0000",
"end": "2023-11-01T20:45:00+0000",
"all_day": False,
"no_end_time": False,
},
"event": {
"_time_to_be_confirmed": True,
},
}
assert "Date: 01/11/2023 00:00 to 02/11/2023 02:15 (Asia/Calcutta) (Time to be confirmed)" == format_event_datetime(
event3
)

# Case 4: Event with no end time
event4 = {
"dates": {
"tz": "Asia/Calcutta",
"start": "2023-10-31T18:30:00+0000",
"all_day": False,
"no_end_time": True,
}
}
assert "Date: 01/11/2023 00:00 (Asia/Calcutta)" == format_event_datetime(event4)

# Case 5: All-day event with no_end_time
event5 = {
"dates": {
"tz": "Asia/Calcutta",
"start": "2023-10-31T18:30:00+0000",
"end": "2023-11-01T18:29:59+0000",
"all_day": True,
"no_end_time": True,
},
}
assert "Date: 01/11/2023 00:00 (Asia/Calcutta)" == format_event_datetime(event5)

# Case 6: Multi-day event
event6 = {
"dates": {
"tz": "Asia/Calcutta",
"start": "2023-10-31T18:30:00+0000",
"end": "2023-11-02T20:45:00+0000",
"all_day": False,
"no_end_time": False,
},
}
assert "Date: 01/11/2023 00:00 to 03/11/2023 02:15 (Asia/Calcutta)" == format_event_datetime(event6)

# Case 7: REGULAR schedule_type with end_time
event7 = {
"dates": {
"tz": "Asia/Calcutta",
"start": "2023-11-01T19:29:59+0000",
"end": "2023-11-02T15:30:00+0000",
"all_day": False,
"no_end_time": False,
}
}
assert "Time: 00:59 AM to 21:00 PM on Date: November 2, 2023 (Asia/Calcutta)" == format_event_datetime(event7)

# Case 8: REGULAR schedule_type with no end time
event8 = {
"dates": {
"tz": "Asia/Calcutta",
"start": "2023-11-01T18:30:00+0000",
"all_day": False,
"no_end_time": True,
},
}
assert "Date: 02/11/2023 00:00 (Asia/Calcutta)" == format_event_datetime(event8)

0 comments on commit 431a1df

Please sign in to comment.