Coverage for src / mesh / templatetags / mesh_tags.py: 73%

108 statements  

« prev     ^ index     » next       coverage.py v7.13.1, created at 2026-10-22 07:20 +0000

1import datetime 

2import re 

3from logging import getLogger 

4 

5from dateutil.relativedelta import relativedelta 

6from django import template 

7from django.http import HttpRequest 

8from django.template.defaultfilters import stringfilter 

9from django.template.loader import render_to_string 

10from django.utils.html import escape, mark_safe 

11from opentelemetry.trace import get_tracer 

12 

13from mesh.models.file_helpers import file_name as original_file_name 

14from mesh.models.orm.submission_models import SUBMISSION_STATE_CHOICES 

15from mesh.models.user.user_interfaces import ImpersonateData 

16from mesh.views.components.icon import Icon 

17from mesh.views.forms.role_forms import RoleSwitchForm 

18 

19register = template.Library() 

20 

21logger = getLogger(__name__) 

22tracer = get_tracer(__name__) 

23 

24 

25@register.filter 

26def render_attrs(attrs): 

27 """Render a Dict[str, List[str]] as HTML attributes.""" 

28 try: 

29 parts = [] 

30 for attr, values in attrs.items(): 

31 value = " ".join(str(v) for v in values) 

32 parts.append(f'{attr}="{value}"') 

33 return mark_safe(" ".join(parts)) 

34 except TypeError: 

35 return "" 

36 except AttributeError: 

37 return "" 

38 

39 

40@register.filter 

41@stringfilter 

42def file_name(file_path: str) -> str: 

43 """ 

44 Returns the base name of a file from its path. 

45 """ 

46 return original_file_name(file_path) 

47 

48 

49@register.filter 

50@stringfilter 

51def role_switch_form(role_code: str) -> RoleSwitchForm: 

52 """ 

53 Returns a RoleSwitchForm whose POST switches the user current role to 

54 the provided role code. 

55 """ 

56 return RoleSwitchForm(initial={"role_code": role_code}) 

57 

58 

59@register.simple_tag(takes_context=True) 

60def check_rights(context, function_name: str, *args, **kwargs) -> bool: 

61 """ 

62 Wrapper around `RoleHandler.check_rights` method for use in template. 

63 """ 

64 role = context["request"].current_role 

65 fct = getattr(role, function_name, None) 

66 if not fct: 

67 logger.error(f"function {function_name} not found for role {role}") 

68 return False 

69 return fct(*args, **kwargs) 

70 

71 

72@register.filter 

73def get_impersonate_data(request: HttpRequest) -> ImpersonateData | None: 

74 return ImpersonateData.from_session(request.session) 

75 

76 

77@register.filter 

78@stringfilter 

79def filter_email(value): 

80 new_value = re.sub(r"\(.*\@.*\)", "", value) 

81 return new_value 

82 

83 

84@register.filter 

85def get_delay(date): 

86 today = datetime.date.today() 

87 

88 # delta = date - today 

89 delta = relativedelta(date, today) 

90 

91 days = delta.days 

92 weeks = delta.weeks 

93 months = delta.months 

94 years = delta.years 

95 abs_days = abs(days) 

96 abs_weeks = abs(weeks) 

97 abs_months = abs(months) 

98 abs_years = abs(years) 

99 

100 badge_class = "success" 

101 

102 if date == today: 

103 value = "today" 

104 badge_class = "warning" 

105 elif days == 1 and months == 0: 

106 value = "tomorrow" 

107 badge_class = "warning" 

108 elif days == -1 and months == 0: 

109 value = "yesterday" 

110 badge_class = "error" 

111 else: 

112 if abs_years == 1: 

113 value = "1 year" 

114 elif abs_years > 1: 

115 value = f"{abs_years} years" 

116 elif abs_months == 1: 

117 value = "1 month" 

118 elif abs_months > 1: 

119 value = f"{abs_months} months" 

120 elif days % 7 == 0: 

121 if abs_weeks == 1: 

122 value = "1 week" 

123 else: 

124 value = f"{abs_weeks} weeks" 

125 else: 

126 value = f"{abs_days} days" 

127 if abs_days < 7: 

128 badge_class = "warning" 

129 

130 if days > 0 or months > 0 or years > 1: 

131 value = "in " + value 

132 else: 

133 value = value + " ago" 

134 badge_class = "error" 

135 

136 value = f'<span class="badge rounded-pill badge-{badge_class}">{value}</span>' 

137 return value 

138 

139 

140@register.filter 

141@stringfilter 

142def get_state_display(value): 

143 results = [s[1] for s in SUBMISSION_STATE_CHOICES if s[0] == value] 

144 if len(results) > 0: 

145 value = results[0] 

146 return value 

147 

148 

149@register.simple_tag 

150def render_icon_escaped(icon: Icon): 

151 html = render_to_string( 

152 "mesh/components/icon.html", 

153 { 

154 "icon_class": icon.icon_class, 

155 "icon_attrs": icon.icon_attrs, 

156 }, 

157 ) 

158 return escape(html)