Coverage for src/django_resume/views.py: 100%

66 statements  

« prev     ^ index     » next       coverage.py v7.6.1, created at 2024-10-13 13:17 +0200

1from typing import Any 

2 

3from django.contrib.auth.decorators import login_required 

4from django import forms 

5from django.http import HttpRequest, HttpResponse 

6from django.shortcuts import render, get_object_or_404 

7from django.views.decorators.http import require_http_methods 

8 

9from .models import Resume 

10from .plugins import plugin_registry 

11 

12 

13def get_edit_and_show_urls(request: HttpRequest) -> tuple[str, str]: 

14 query_params = request.GET.copy() 

15 if "edit" in query_params: 

16 query_params.pop("edit") 

17 

18 show_url = f"{request.path}?{query_params.urlencode()}" 

19 query_params["edit"] = "true" 

20 edit_url = f"{request.path}?{query_params.urlencode()}" 

21 return edit_url, show_url 

22 

23 

24def resume_cv(request: HttpRequest, slug: str) -> HttpResponse: 

25 """ 

26 Show a CV view of the resume. 

27 

28 By default, you need a token to be able to see the CV. 

29 """ 

30 resume = get_object_or_404(Resume.objects.select_related("owner"), slug=slug) 

31 

32 edit = bool(dict(request.GET).get("edit", False)) 

33 is_editable = request.user.is_authenticated and resume.owner == request.user 

34 show_edit_button = True if is_editable and edit else False 

35 

36 edit_url, show_url = get_edit_and_show_urls(request) 

37 context = { 

38 "resume": resume, 

39 "timelines": [], 

40 "projects": [], 

41 # needed to include edit styles in the base template 

42 "show_edit_button": show_edit_button, 

43 "is_editable": is_editable, 

44 "edit_url": edit_url, 

45 "show_url": show_url, 

46 } 

47 for plugin in plugin_registry.get_all_plugins(): 

48 context[plugin.name] = plugin.get_context( 

49 request, 

50 plugin.get_data(resume), 

51 resume.pk, 

52 context={}, 

53 edit=show_edit_button, 

54 ) 

55 return render(request, "django_resume/plain/resume_cv.html", context=context) 

56 

57 

58def resume_detail(request: HttpRequest, slug: str) -> HttpResponse: 

59 """ 

60 The main resume detail view. 

61 

62 At the moment, it is used for the cover letter. 

63 """ 

64 resume = get_object_or_404(Resume.objects.select_related("owner"), slug=slug) 

65 

66 edit = bool(dict(request.GET).get("edit", False)) 

67 is_editable = request.user.is_authenticated and resume.owner == request.user 

68 show_edit_button = True if is_editable and edit else False 

69 

70 edit_url, show_url = get_edit_and_show_urls(request) 

71 context = { 

72 "resume": resume, 

73 # needed to include edit styles in the base template 

74 "show_edit_button": show_edit_button, 

75 "is_editable": is_editable, 

76 "edit_url": edit_url, 

77 "show_url": show_url, 

78 } 

79 plugin_names = ["about", "identity", "cover"] 

80 for name in plugin_names: 

81 plugin = plugin_registry.get_plugin(name) 

82 context[plugin.name] = plugin.get_context( 

83 request, 

84 plugin.get_data(resume), 

85 resume.pk, 

86 context={}, 

87 edit=show_edit_button, 

88 ) 

89 return render(request, "django_resume/plain/resume_detail.html", context=context) 

90 

91 

92class ResumeForm(forms.ModelForm): 

93 class Meta: 

94 model = Resume 

95 fields = ["name", "slug"] 

96 

97 

98@login_required 

99@require_http_methods(["GET", "POST"]) 

100def resume_list(request: HttpRequest) -> HttpResponse: 

101 """ 

102 The main resume list view. Only authenticated users can see it. 

103 

104 You can add and delete your resumes from this view. 

105 """ 

106 assert request.user.is_authenticated # type guard just to make mypy happy 

107 my_resumes = Resume.objects.filter(owner=request.user) 

108 context: dict[str, Any] = { 

109 "is_editable": True, # needed to include edit styles in the base 

110 "resumes": my_resumes, 

111 "form": ResumeForm(), 

112 } 

113 if request.method == "POST": 

114 form = ResumeForm(request.POST) 

115 context["form"] = form 

116 if form.is_valid(): 

117 resume = form.save(commit=False) 

118 resume.owner = request.user 

119 resume.save() 

120 context["new_resume"] = resume 

121 return render( 

122 request, "django_resume/plain/resume_list_main.html", context=context 

123 ) 

124 else: 

125 # just render the complete template on GET 

126 return render(request, "django_resume/plain/resume_list.html", context=context) 

127 

128 

129@login_required 

130@require_http_methods(["DELETE"]) 

131def resume_delete(request: HttpRequest, slug: str) -> HttpResponse: 

132 """ 

133 Delete a resume. 

134 

135 Only the owner of the resume can delete it. 

136 """ 

137 resume = get_object_or_404(Resume, slug=slug) 

138 if resume.owner != request.user: 

139 return HttpResponse(status=403) 

140 

141 resume.delete() 

142 return HttpResponse(status=200) # 200 instead of 204 for htmx compatibility