Skip to content

Commit e2c683e

Browse files
authored
Merge pull request #1565 from ManushiM/user-profiles
New ArcGIS notebook to validate User profiles
2 parents 51f03e0 + 039bd49 commit e2c683e

File tree

1 file changed

+352
-0
lines changed

1 file changed

+352
-0
lines changed
Lines changed: 352 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,352 @@
1+
{
2+
"cells": [
3+
{
4+
"cell_type": "markdown",
5+
"metadata": {
6+
"toc": true
7+
},
8+
"source": [
9+
"<h1>Table of Contents<span class=\"tocSkip\"></span></h1>\n",
10+
"<div class=\"toc\"><ul class=\"toc-item\"></ul></div>"
11+
]
12+
},
13+
{
14+
"cell_type": "markdown",
15+
"metadata": {},
16+
"source": [
17+
"# Administration: Validate user profiles\n",
18+
"\n",
19+
"* 👟 Ready To Run!\n",
20+
"* 🗃️ Administration\n",
21+
"* 👤 User Management\n",
22+
"\n",
23+
"__Requirements__\n",
24+
"* 🔒 Administrator Privileges\n",
25+
"\n",
26+
"Some organizations require member profiles to contain values beyond the minumum required attributes necessary to create a valid user in an ArcGIS Online Organization or ArcGIS Enterprise. For instance, in order to comply with policies and regulations, an organization may require a profile picture or a brief description. This notebook will check attribute values for users of the organization to monitor profiles."
27+
]
28+
},
29+
{
30+
"cell_type": "markdown",
31+
"metadata": {},
32+
"source": [
33+
"To get started, let's import the necessary libraries and connect to our GIS."
34+
]
35+
},
36+
{
37+
"cell_type": "code",
38+
"execution_count": 1,
39+
"metadata": {},
40+
"outputs": [],
41+
"source": [
42+
"import os\n",
43+
"import datetime as dt\n",
44+
"\n",
45+
"import pandas as pd\n",
46+
"\n",
47+
"from arcgis.gis import GIS"
48+
]
49+
},
50+
{
51+
"cell_type": "code",
52+
"execution_count": 2,
53+
"metadata": {},
54+
"outputs": [],
55+
"source": [
56+
"gis = GIS(\"home\")"
57+
]
58+
},
59+
{
60+
"cell_type": "markdown",
61+
"metadata": {},
62+
"source": [
63+
"Then, let's create a list containing strings that represent all the values that your organization requires (beyond the required attributes for a valid user profile).\n",
64+
"\n",
65+
"`access_type` refers to the `User.access` property of a user and it indicates the level of access of the user: private, org, or public.\n",
66+
"\n",
67+
"While `access_type` may not be a requirement for your organization to validate a user profile, we have added it here to demonstrate how other useful properties can also be extracted for each user profile to learn more about them. "
68+
]
69+
},
70+
{
71+
"cell_type": "code",
72+
"execution_count": 3,
73+
"metadata": {},
74+
"outputs": [],
75+
"source": [
76+
"complete_profile = ['description', 'thumbnail', 'access', 'access_type']"
77+
]
78+
},
79+
{
80+
"cell_type": "markdown",
81+
"metadata": {},
82+
"source": [
83+
"We'll define a function that loops through the list we created and inspects a user object for the values of these attributes. We'll create a list of True/False values for each user regarding the necessary attributes."
84+
]
85+
},
86+
{
87+
"cell_type": "code",
88+
"execution_count": 4,
89+
"metadata": {},
90+
"outputs": [],
91+
"source": [
92+
"def get_missing_profile_attrs(member):\n",
93+
" non_compliance = []\n",
94+
" for attr in complete_profile:\n",
95+
" if attr=='access_type':\n",
96+
" non_compliance.append(member.access)\n",
97+
" else:\n",
98+
" if getattr(member, attr) == None: \n",
99+
" non_compliance.append(False)\n",
100+
" else:\n",
101+
" non_compliance.append(True)\n",
102+
" return non_compliance"
103+
]
104+
},
105+
{
106+
"cell_type": "markdown",
107+
"metadata": {},
108+
"source": [
109+
"Now we can create a list of users in the GIS and loop through them, calling the function written above to inspect each user object. This will create a dictionary with usernames as the _key_ and the list of True/False status for the required attributes as _values_."
110+
]
111+
},
112+
{
113+
"cell_type": "code",
114+
"execution_count": 5,
115+
"metadata": {},
116+
"outputs": [],
117+
"source": [
118+
"user_profile_status = {}\n",
119+
"for user in gis.users.search(\"NOT esri_*\"):\n",
120+
" missing_profile_atts = get_missing_profile_attrs(user)\n",
121+
" user_profile_status[user.username] = missing_profile_atts"
122+
]
123+
},
124+
{
125+
"cell_type": "markdown",
126+
"metadata": {},
127+
"source": [
128+
"The pandas library can be used to create a dataframe from the above dictionary."
129+
]
130+
},
131+
{
132+
"cell_type": "code",
133+
"execution_count": 6,
134+
"metadata": {},
135+
"outputs": [
136+
{
137+
"data": {
138+
"text/html": [
139+
"<div>\n",
140+
"<style scoped>\n",
141+
" .dataframe tbody tr th:only-of-type {\n",
142+
" vertical-align: middle;\n",
143+
" }\n",
144+
"\n",
145+
" .dataframe tbody tr th {\n",
146+
" vertical-align: top;\n",
147+
" }\n",
148+
"\n",
149+
" .dataframe thead th {\n",
150+
" text-align: right;\n",
151+
" }\n",
152+
"</style>\n",
153+
"<table border=\"1\" class=\"dataframe\">\n",
154+
" <thead>\n",
155+
" <tr style=\"text-align: right;\">\n",
156+
" <th></th>\n",
157+
" <th>description</th>\n",
158+
" <th>thumbnail</th>\n",
159+
" <th>access</th>\n",
160+
" <th>access_type</th>\n",
161+
" </tr>\n",
162+
" </thead>\n",
163+
" <tbody>\n",
164+
" <tr>\n",
165+
" <th>achapkowski_geosaurus</th>\n",
166+
" <td>False</td>\n",
167+
" <td>False</td>\n",
168+
" <td>True</td>\n",
169+
" <td>org</td>\n",
170+
" </tr>\n",
171+
" <tr>\n",
172+
" <th>amani_geosaurus</th>\n",
173+
" <td>True</td>\n",
174+
" <td>True</td>\n",
175+
" <td>True</td>\n",
176+
" <td>public</td>\n",
177+
" </tr>\n",
178+
" <tr>\n",
179+
" <th>andrew57</th>\n",
180+
" <td>True</td>\n",
181+
" <td>True</td>\n",
182+
" <td>True</td>\n",
183+
" <td>private</td>\n",
184+
" </tr>\n",
185+
" <tr>\n",
186+
" <th>api_data_owner</th>\n",
187+
" <td>False</td>\n",
188+
" <td>False</td>\n",
189+
" <td>True</td>\n",
190+
" <td>org</td>\n",
191+
" </tr>\n",
192+
" <tr>\n",
193+
" <th>arcgis_python</th>\n",
194+
" <td>True</td>\n",
195+
" <td>True</td>\n",
196+
" <td>True</td>\n",
197+
" <td>public</td>\n",
198+
" </tr>\n",
199+
" </tbody>\n",
200+
"</table>\n",
201+
"</div>"
202+
],
203+
"text/plain": [
204+
" description thumbnail access access_type\n",
205+
"achapkowski_geosaurus False False True org\n",
206+
"amani_geosaurus True True True public\n",
207+
"andrew57 True True True private\n",
208+
"api_data_owner False False True org\n",
209+
"arcgis_python True True True public"
210+
]
211+
},
212+
"execution_count": 6,
213+
"metadata": {},
214+
"output_type": "execute_result"
215+
}
216+
],
217+
"source": [
218+
"user_profile_df = pd.DataFrame(data=user_profile_status, index=complete_profile).T\n",
219+
"user_profile_df.head()"
220+
]
221+
},
222+
{
223+
"cell_type": "markdown",
224+
"metadata": {},
225+
"source": [
226+
"This dataframe can then be written to a _.csv._ file on your fileshare,"
227+
]
228+
},
229+
{
230+
"cell_type": "code",
231+
"execution_count": 7,
232+
"metadata": {},
233+
"outputs": [],
234+
"source": [
235+
"output_dir = \"/arcgis/home/\"\n",
236+
"current = str(int(dt.datetime.now().timestamp()))\n",
237+
"out_file = \"output_org_user_profile\" + \"_\" + current + \".csv\"\n",
238+
"\n",
239+
"user_profile_df.to_csv(os.path.join(output_dir, out_file), index_label='username')"
240+
]
241+
},
242+
{
243+
"cell_type": "markdown",
244+
"metadata": {},
245+
"source": [
246+
"and the dataframe can be written to a _.csv_ file item on your Organization."
247+
]
248+
},
249+
{
250+
"cell_type": "code",
251+
"execution_count": 8,
252+
"metadata": {},
253+
"outputs": [
254+
{
255+
"data": {
256+
"text/html": [
257+
"<div class=\"item_container\" style=\"height: auto; overflow: hidden; border: 1px solid #cfcfcf; border-radius: 2px; background: #f6fafa; line-height: 1.21429em; padding: 10px;\">\n",
258+
" <div class=\"item_left\" style=\"width: 210px; float: left;\">\n",
259+
" <a href='https://geosaurus.maps.arcgis.com/home/item.html?id=9f077933498c41fbb222ee95fee23102' target='_blank'>\n",
260+
" <img src='http://static.arcgis.com/images/desktopapp.png' class=\"itemThumbnail\">\n",
261+
" </a>\n",
262+
" </div>\n",
263+
"\n",
264+
" <div class=\"item_right\" style=\"float: none; width: auto; overflow: hidden;\">\n",
265+
" <a href='https://geosaurus.maps.arcgis.com/home/item.html?id=9f077933498c41fbb222ee95fee23102' target='_blank'><b>output_org_user_profile_1686349431</b>\n",
266+
" </a>\n",
267+
" <br/><img src='https://geosaurus.maps.arcgis.com/home/js/jsapi/esri/css/images/item_type_icons/layers16.png' style=\"vertical-align:middle;\">CSV by MMajumdar_geosaurus\n",
268+
" <br/>Last Modified: June 09, 2023\n",
269+
" <br/>0 comments, 0 views\n",
270+
" </div>\n",
271+
" </div>\n",
272+
" "
273+
],
274+
"text/plain": [
275+
"<Item title:\"output_org_user_profile_1686349431\" type:CSV owner:MMajumdar_geosaurus>"
276+
]
277+
},
278+
"execution_count": 8,
279+
"metadata": {},
280+
"output_type": "execute_result"
281+
}
282+
],
283+
"source": [
284+
"gis.content.add({}, output_dir + out_file)"
285+
]
286+
},
287+
{
288+
"cell_type": "markdown",
289+
"metadata": {},
290+
"source": [
291+
"You may download this item if you wish, and if you decide to delete this item after having used it, you may run the script below by updating the `item_id` with the id of this file in your organization."
292+
]
293+
},
294+
{
295+
"cell_type": "code",
296+
"execution_count": null,
297+
"metadata": {},
298+
"outputs": [],
299+
"source": [
300+
"item = gis.content.get(item_id)\n",
301+
"item.delete()"
302+
]
303+
},
304+
{
305+
"cell_type": "markdown",
306+
"metadata": {},
307+
"source": [
308+
"# Conclusion\n",
309+
"\n",
310+
"This notebook checked attribute values for an organization's users and wrote the results to a _.csv_ file. This file can then be analyzed to validate that all user profiles contain the minimum required attributes as defined by any policies or regulations."
311+
]
312+
}
313+
],
314+
"metadata": {
315+
"esriNotebookRuntime": {
316+
"notebookRuntimeName": "ArcGIS Notebook Python 3 Standard",
317+
"notebookRuntimeVersion": "7.0"
318+
},
319+
"kernelspec": {
320+
"display_name": "Python 3 (ipykernel)",
321+
"language": "python",
322+
"name": "python3"
323+
},
324+
"language_info": {
325+
"codemirror_mode": {
326+
"name": "ipython",
327+
"version": 3
328+
},
329+
"file_extension": ".py",
330+
"mimetype": "text/x-python",
331+
"name": "python",
332+
"nbconvert_exporter": "python",
333+
"pygments_lexer": "ipython3",
334+
"version": "3.7.16"
335+
},
336+
"toc": {
337+
"base_numbering": 1,
338+
"nav_menu": {},
339+
"number_sections": true,
340+
"sideBar": true,
341+
"skip_h1_title": true,
342+
"title_cell": "Table of Contents",
343+
"title_sidebar": "Contents",
344+
"toc_cell": true,
345+
"toc_position": {},
346+
"toc_section_display": true,
347+
"toc_window_display": true
348+
}
349+
},
350+
"nbformat": 4,
351+
"nbformat_minor": 2
352+
}

0 commit comments

Comments
 (0)