Skip to content

Commit 8b789ca

Browse files
authored
Merge pull request #561 from Esmat-Farjad/feat/cluster_users
the cluster user added and the problem with granting access to user s…
2 parents 6ed24d9 + 5fcce8d commit 8b789ca

File tree

15 files changed

+636
-154
lines changed

15 files changed

+636
-154
lines changed

src/project_reports/templates/project_reports/cluster_5w_dashboard.html

+7-3
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,17 @@
2222
<div class="top-panel pb-2 pt-4 flex flex-col items-center justify-between gap-2 sm:flex-row">
2323
<h2 class="locations text-red-be">{{ request.resolver_match.kwargs.cluster|capfirst }} 5W Dashboard</h2>
2424
<div class="actions-panel">
25-
<a href="{% url 'export-organization-partners' request.resolver_match.kwargs.cluster %}?{{request.GET.urlencode}}" class="btn btn-gray">
25+
<a href="{% url 'export-organization-partners' request.resolver_match.kwargs.cluster %}?{{request.GET.urlencode}}"
26+
class="tooltip btn btn-gray">
2627
Export Organizations
2728
<span class="icon-download"></span>
29+
<span class="tooltip-text">Download the list of organizations that have submitted reports.</span>
2830
</a>
29-
<a href="{% url 'export-focal-persons' request.resolver_match.kwargs.cluster %}?{{request.GET.urlencode}}" class="btn btn-gray">
30-
Export Users
31+
<a href="{% url 'export-focal-persons' request.resolver_match.kwargs.cluster %}?{{request.GET.urlencode}}"
32+
class="tooltip btn btn-gray">
33+
Export Focal Points
3134
<span class="icon-download"></span>
35+
<span class="tooltip-text">Download the list of focal points who have submitted reports. </span>
3236
</a>
3337
<a class="export-button btn btn-red"
3438
href="{% url 'export-cluster-5w-dashboard' request.resolver_match.kwargs.cluster %}?{{request.GET.urlencode}}"

src/project_reports/templates/project_reports/org_5w_dashboard.html

+1-32
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,7 @@ <h3 class="text-lg font-medium pb-2 ">People Reached Monthly Trend</h3>
169169
</p>
170170
</div>
171171
<div class="mt-4">
172-
<canvas id="myChart" style="max-width: 100%;"></canvas>
172+
{{ line_chart|safe }}
173173
</div>
174174
</div>
175175
</div>
@@ -182,35 +182,4 @@ <h3 class="text-lg font-medium pb-2 ">People Reached Monthly Trend</h3>
182182
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"
183183
integrity="sha256-20nQCchB9co0qIjJZRGuk2/Z9VM+kNiyxNV1lvTlZBo="
184184
crossorigin=""></script>
185-
186-
<script>
187-
const ctx = document.getElementById('myChart').getContext("2d");
188-
new Chart(ctx, {
189-
type: 'line',
190-
data: {
191-
labels: {{people_reached_labels|safe}},
192-
datasets: [{
193-
label: 'People Reached',
194-
data: {{people_reached_data|safe}},
195-
borderColor:"#a52824",
196-
backgroundColor:"rgba(255,0,0, 0.1)",
197-
pointBackgroundColor:"#a52824",
198-
fill: true,
199-
200-
tension: 0.4,
201-
borderWidth: 1
202-
}]
203-
},
204-
options: {
205-
maintainAspectRatio: false,
206-
responsive: true,
207-
height: 300,
208-
scales: {
209-
y: {
210-
beginAtZero: true
211-
}
212-
}
213-
}
214-
});
215-
</script>
216185
{% endblock scripts %}

src/project_reports/utils.py

+1
Original file line numberDiff line numberDiff line change
@@ -466,6 +466,7 @@ def write_import_report_template_sheet(workbook, monthly_report):
466466

467467
def get_project_reporting_months(project):
468468
start_date = project.start_date
469+
start_date = start_date.replace(day=1)
469470
end_date = project.end_date
470471
today = now()
471472
current_date = start_date

src/project_reports/views/dashboards.py

+27-21
Original file line numberDiff line numberDiff line change
@@ -116,26 +116,7 @@ def cluster_5w_dashboard(request, cluster):
116116
if total_reached and all(value is not None for value in total_reached.values()):
117117
sum_disaggregation = sum(value for value in total_reached.values() if value is not None)
118118
data_dict[category]["total"] = sum_disaggregation
119-
line_chart = go.Figure()
120-
# Plot each metric as a line
121-
line_chart.add_trace(
122-
go.Scatter(
123-
x=labels,
124-
y=data,
125-
mode="lines+markers",
126-
name="Projects Monthly Reached",
127-
hovertemplate="<b>Months:</b> %{x}<br><b>Beneficiary Reached:</b> %{y}<br><extra></extra>",
128-
line=dict(shape="spline", color="#a52824"),
129-
)
130-
)
131-
# Update layout
132-
line_chart.update_layout(
133-
xaxis_title="Month Names",
134-
yaxis_title="Beneficiary Reached",
135-
showlegend=True,
136-
margin=dict(r=0, t=0, b=0, l=0),
137-
height=400,
138-
)
119+
line_chart = get_line_chart(data, labels)
139120
context = {
140121
"cluster": cluster,
141122
"counts": counts,
@@ -245,7 +226,7 @@ def org_5w_dashboard(request, code):
245226
if total_reached and all(value is not None for value in total_reached.values()):
246227
sum_disaggregation = sum(value for value in total_reached.values() if value is not None)
247228
data_dict[category]["total"] = sum_disaggregation
248-
229+
line_chart = get_line_chart(data, labels)
249230
context = {
250231
"org": org,
251232
"counts": counts,
@@ -254,6 +235,31 @@ def org_5w_dashboard(request, code):
254235
"activity_domains": activity_domains,
255236
"reach_by_activity": data_dict,
256237
"dashboard_filter": monthly_report_filter,
238+
"line_chart": line_chart.to_html(),
257239
}
258240

259241
return render(request, "project_reports/org_5w_dashboard.html", context)
242+
243+
244+
def get_line_chart(data, labels):
245+
line_chart = go.Figure()
246+
# Plot each metric as a line
247+
line_chart.add_trace(
248+
go.Scatter(
249+
x=labels,
250+
y=data,
251+
mode="lines+markers",
252+
name="Projects Monthly Reached",
253+
hovertemplate="<b>Months:</b> %{x}<br><b>Beneficiary Reached:</b> %{y}<br><extra></extra>",
254+
line=dict(shape="spline", color="#a52824"),
255+
)
256+
)
257+
# Update layout
258+
line_chart.update_layout(
259+
xaxis_title="Month Names",
260+
yaxis_title="Beneficiary Reached",
261+
showlegend=False,
262+
margin=dict(r=0, t=0, b=0, l=0),
263+
height=400,
264+
)
265+
return line_chart

src/rh/models.py

+7-1
Original file line numberDiff line numberDiff line change
@@ -629,7 +629,13 @@ def get_available_states(self):
629629
return [state[0] for state in STATES]
630630

631631
def __str__(self):
632-
return f"Target Location: {self.province}, {self.district}"
632+
location_str = f"Target Location: {self.province}, {self.district}"
633+
if self.facility_name:
634+
location_str += f", {self.facility_name} ({self.facility_id})"
635+
return location_str
636+
637+
# def __str__(self):
638+
# return f"Target Location: {self.province}, {self.district}"
633639

634640
class Meta:
635641
verbose_name = "Target Location"

src/rh/templates/rh/clusters/cluster_home.html

+6
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,12 @@
2424
<div class="holder">
2525
<h1 class="locations text-red-be"> {{ request.resolver_match.kwargs.cluster|upper }}'s Home </h1>
2626
<ul class="buttons-list-inline">
27+
<li>
28+
{% if request.resolver_match.kwargs.cluster %}
29+
<a href="{% url 'cluster-users' request.resolver_match.kwargs.cluster %}"
30+
class="btn btn-gray-outline" >Cluster Users</a>
31+
{% endif %}
32+
</li>
2733
<li>
2834
{% if request.resolver_match.kwargs.cluster %}
2935
<a href="{% url 'cluster-projects-list' request.resolver_match.kwargs.cluster %}" class="btn btn-gray-outline" >Cluster Projects</a>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,204 @@
1+
{% extends "_base.html" %}
2+
3+
{% load static %}
4+
{% load humanize %}
5+
{% block title %}
6+
{{cluster.code}} Members
7+
{% endblock title %}
8+
9+
{% block breadcrumb_li %}
10+
<li class=""><a href="{% url 'clusters-home' cluster.code %}">
11+
{{ request.resolver_match.kwargs.cluster|upper }}'s Home
12+
</a></li>
13+
<li class="current">Members</a>
14+
{% endblock %}
15+
16+
{% block content %}
17+
<div class="container">
18+
<div class="projects-options-panel">
19+
<div class="holder">
20+
<div class="input-holder">
21+
<h2 class="text-red-be">{{ cluster.code|upper }}'s Members</h2>
22+
</div>
23+
</div>
24+
<p class="py-4 rounded flex items-center gap-2">
25+
<span class="icon-info"></span>
26+
The below table list your organization's users.
27+
You can filter the users by their using the filter sidebar.
28+
The actions column in the table lets gives you ability to activate and deactivate users.
29+
</p>
30+
</div>
31+
32+
<div class="tabs-nav-holder">
33+
<ul class="tabs-nav">
34+
<li class=" {% if not request.GET.is_active %}active{% endif %}">
35+
<a href="{% url 'users-organization' %}">All Users
36+
({{ users_count }})
37+
</a>
38+
</li>
39+
<li class=" {% if request.GET.is_active == 'true' %}active{% endif %}">
40+
<a href="{{ request.GET.url }}?is_active=true">Active Users
41+
(
42+
{% if active_users_count %}
43+
{{ active_users_count }}
44+
{% else %}
45+
{{ active_users | length }}
46+
{% endif %}
47+
)
48+
</a>
49+
</li>
50+
<li class=" {% if request.GET.is_active == 'false' %}active{% endif %}">
51+
<a href="{{ request.GET.url }}?is_active=false">Deactivated Users
52+
(
53+
{% if deactive_users_count %}{{ deactive_users_count }}{% endif %}
54+
)
55+
</a>
56+
</li>
57+
</ul>
58+
</div>
59+
<div class="">
60+
<div class="table-container">
61+
{% comment %} Table Actions and filters {% endcomment %}
62+
<div class="page-top-panel bottom-border">
63+
<!-- filter applied -->
64+
<div class="filter-container" id="filter-container">
65+
{% for field in users_filter.form %}
66+
{% if forloop.first %}<span>Applied Filters:</span>{% endif %}
67+
{% if field.value and field.name != 'state' %}
68+
69+
<span class="filter-item title"
70+
id="title">{{ field.name }}</span>
71+
{% if forloop.last %}<span class="close-alert-message">x</span>{% endif %}
72+
{% endif %}
73+
74+
{% endfor %}
75+
</div>
76+
<!-- filter applied end -->
77+
78+
<div class="actions-panel">
79+
<a href="{% url 'export-cluster-users' cluster.code %}?{{request.GET.urlencode}}"
80+
class="btn btn-gray tooltip" onclick="return confirm('Are you sure you want to export {{cluster.code|upper}} users?')">
81+
Export <span class="icon-download"></span>
82+
<span class="tooltip-text">Download your cluster users</span>
83+
</a>
84+
{% if users_filter %}
85+
{% include "components/_filter_drawer.html" with filter=users_filter %}
86+
{% endif %}
87+
</div>
88+
</div>
89+
90+
{% comment %} Table {% endcomment %}
91+
{% if users %}
92+
<div class="table-wrapper-scrollable ">
93+
<table class="table all-projects-table " >
94+
<thead class="content-block ">
95+
<tr>
96+
<th></th>
97+
<th>
98+
<strong class="table-title">Name</strong>
99+
</th>
100+
<th class="description-col">
101+
<strong class="table-title">Email</strong>
102+
</th>
103+
<th>
104+
<strong class="table-title">Clusters</strong>
105+
</th>
106+
<th>
107+
<strong class="table-title">Date Joined</strong>
108+
</th>
109+
<th>
110+
<strong class="table-title">Last Login</strong>
111+
</th>
112+
<th>
113+
<strong class="table-title">Status</strong>
114+
</th>
115+
<th>
116+
<strong class="table-title">Action</strong>
117+
</th>
118+
</tr>
119+
</thead>
120+
121+
<tbody>
122+
{% for user in users %}
123+
<tr id="user-{{ user.id }}">
124+
<td>
125+
{% for group in user.groups.all %}
126+
{% if group.name == "ORG_LEAD" %}
127+
<span class="completed">Admin</span>
128+
{% endif %}
129+
{% endfor %}
130+
</td>
131+
<td>
132+
<a href="{% url 'cluster-user-profile' user.username %}">
133+
{{ user.first_name }} {{ user.last_name }} <em>({{ user.username }})</em>
134+
</a>
135+
</td>
136+
<td>{{ user.email }}</td>
137+
<td>
138+
{% for cluster in user.profile.clusters.all %}
139+
{{ cluster.title }}
140+
{% if not forloop.last %},{% endif %}
141+
{% endfor %}
142+
</td>
143+
<td>{{ user.date_joined|naturaltime }}</td>
144+
<td>{{ user.last_login|naturaltime }}</td>
145+
<td>
146+
{% if user.is_active %}
147+
<span class="in-progress">Active</span>
148+
{% else %}
149+
<span class="archive">Deactive</span>
150+
{% endif %}
151+
</td>
152+
<td>
153+
<div class="table-flex-holder">
154+
<ul class="table-options-buttons">
155+
{% if perms.rh.activate_deactivate_user %}
156+
<li>
157+
<button id="dectivate-button-{{ user.id }}"
158+
class="btn btn-gray"
159+
hx-patch="{% url 'users-toggle-status' user.id %}"
160+
hx-swap="outerHTML"
161+
hx-target="#user-{{ user.id }}"
162+
hx-disable-element="#dectivate-button-{{ user.id }}"
163+
hx-ext="disable-element">
164+
{% if user.is_active %}
165+
<span class="project-detail icon-trashbin"></span> Deactivate
166+
{% else %}
167+
Activate
168+
{% endif %}
169+
<img class="htmx-indicator"
170+
id="spinner"
171+
alt="spinner"
172+
style="width: 1.2rem"
173+
src="{% static 'images/spinner.gif' %}" />
174+
</button>
175+
</li>
176+
{% endif %}
177+
</ul>
178+
</div>
179+
</td>
180+
</tr>
181+
{% endfor %}
182+
</tbody>
183+
</table>
184+
</div>
185+
{% else %}
186+
<div class="view_nocontent">
187+
<div class="nocontent_help">
188+
<h4>No users found.</h4>
189+
<p>Ask your organizations members to register for a new account in ReportHub.</p>
190+
</div>
191+
</div>
192+
{% endif %}
193+
194+
{% comment %} Pagination {% endcomment %}
195+
{% include "components/_pagination.html" with object_list=users %}
196+
</div>
197+
198+
</div>
199+
200+
</div>
201+
{% endblock content %}
202+
203+
{% block scripts %}
204+
{% endblock scripts %}

src/rh/urls.py

+1
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@
6262
),
6363
path("projects/clusters", projects.users_clusters_projects_list, name="user-clusters-projects-list"),
6464
path("projects/clusters/<str:cluster>", projects.cluster_projects_list, name="cluster-projects-list"),
65+
path("users/clusters/<str:cluster>", projects.cluster_users, name="cluster-users"),
6566
# cluster
6667
path("clusters/<str:cluster>", clusters_views.cluster_home, name="clusters-home"),
6768
path("clusters/<str:cluster>/reports", clusters_views.pending_reports, name="clusters-reports"),

0 commit comments

Comments
 (0)