External Content
External content provides another way for publishers to deploy apps, reports, and dashboards.
To deploy external content, package your content as a tarball (*.tar.gz
or *.tgz
) and then upload it to Connect. Once deployed, find external content in the Content List. The deployed content respects the same access controls as other Connect content.
Prerequisites
- You must have Publisher or Administrator permissions to add external content to a Connect instance.
Example: Simple external content to list the last ten active users
What if you want to list the last 10 users to log in to your Connect instance? Connect doesn’t directly support this feature. However, creating a Quarto document that uses the Connect public API to list the last 10 users is straightforward. You can create this document and add it as a piece of content to Connect.
Preparing the external content
- Copy the following documents into your project directory:
---
title: "Last 10 Active Users"
---
```{python}
#| echo: false
from IPython.display import Markdown
from datetime import *
from posit.connect import Client
from tabulate import tabulate
import json
import os
import pytz
CONNECT_API_KEY = os.getenv('CONNECT_API_KEY')
CONNECT_SERVER = os.getenv('CONNECT_SERVER')
def sort_by_login(user):
return user['active_time']
def find_users():
with Client(
api_key=CONNECT_API_KEY, url=CONNECT_SERVER
) as client:
find_results = client.users.find()
find_results.sort(key=sort_by_login, reverse=True)
return find_results
def last_active_time(dt):
utc_time = pytz.utc.localize(dt)
local_timezone = pytz.timezone('US/Eastern')
local_time = utc_time.astimezone(local_timezone)
# return local_time.strftime("%Y-%m-%d %-I:%M %p")
return local_time.strftime("%c")
recent_users = find_users()
display_users = []
for user in recent_users[:10]:
local_time = datetime.strptime(user.active_time, '%Y-%m-%dT%H:%M:%SZ')
display_users.append({
'email': user.email,
'name': f'{user.first_name} {user.last_name}',
'username': user.username,
'last_active': last_active_time(local_time)
})
rows = [x.values() for x in display_users]
Markdown(tabulate( rows, headers=["Email", "Name", "Username", "Last Active"]))
```
{
"version": 1,
"locale": "en_US.UTF-8",
"metadata": {
"appmode": "quarto-static"
},
"quarto": {
"version": "1.5.57",
"engines": [
"jupyter"
]
},
"python": {
"version": "3.11.9",
"package_manager": {
"name": "pip",
"version": "24.2",
"package_file": "requirements.txt"
}
},
"files": {
"requirements.txt": {
"checksum": "ff6c426754245786e5d5620c948ac68a"
},
"RecentUsers.qmd": {
"checksum": "f2a7d7999f2cb1aac7d3925bbdc338c7"
}
},
"extension": {
"name": "connect-recent-users",
"title": "Last 10 Active Users",
"description": "Connect Extension: List last 10 active users",
"access_type": "logged_in"
}
}
# requirements.txt generated by rsconnect-python on 2024-09-10 18:51:57.198908
aiofiles==23.2.1
altair==5.2.0
aniso8601==9.0.1
annotated-types==0.7.0
anyio==3.7.1
anywidget==0.9.2
appdirs==1.4.4
appnope==0.1.3
argon2-cffi==23.1.0
argon2-cffi-bindings==21.2.0
arrow==1.3.0
asgiref==3.8.1
asttokens==2.4.1
attrs==23.1.0
beautifulsoup4==4.12.2
bleach==6.1.0
blinker==1.8.2
bokeh==3.4.0
build==1.2.1
cachetools==5.3.3
certifi==2024.8.30
cffi==1.17.1
charset-normalizer==3.3.2
click==8.1.7
comm==0.2.0
commonmark==0.9.1
contourpy==1.2.1
cryptography==43.0.1
debugpy==1.8.0
decorator==5.1.1
defusedxml==0.7.1
distlib==0.3.7
entrypoints==0.4
executing==2.0.1
fastapi==0.103.1
fastapitableau==1.2.0
fastjsonschema==2.19.0
filelock==3.12.2
Flask==3.0.2
flask-restx==1.3.0
fqdn==1.5.1
fsspec==2024.5.0
fuzzbucket-client==0.12.2
gitdb==4.0.11
GitPython==3.1.43
h11==0.14.0
htmltools==0.5.2
httpcore==1.0.5
httpx==0.27.0
humanize==4.9.0
idna==3.6
importlib_metadata==7.1.0
importlib_resources==6.4.0
ipykernel==6.27.1
ipython==8.18.0
ipython-genutils==0.2.0
ipywidgets==8.1.1
isoduration==20.11.0
itsdangerous==2.2.0
jedi==0.19.1
Jinja2==3.1.2
joblib==1.3.2
jsonpointer==2.4
jsonschema==4.20.0
jsonschema-specifications==2023.11.2
jupyter==1.0.0
jupyter-console==6.6.3
jupyter-events==0.9.0
jupyter_client==7.4.9
jupyter_core==5.5.0
jupyter_server==2.12.1
jupyter_server_terminals==0.5.0
jupyterlab-widgets==3.0.9
jupyterlab_pygments==0.3.0
linkify-it-py==2.0.3
markdown-it-py==3.0.0
MarkupSafe==2.1.3
matplotlib-inline==0.1.6
mdit-py-plugins==0.4.1
mdurl==0.1.2
mistune==3.0.2
nbclassic==1.0.0
nbclient==0.9.0
nbconvert==7.12.0
nbformat==5.9.2
nest-asyncio==1.5.8
notebook==6.5.6
notebook_shim==0.2.3
numpy==1.26.4
overrides==7.4.0
packaging==23.2
panda==0.3.1
pandas==2.2.1
pandocfilters==1.5.0
parso==0.8.3
pexpect==4.9.0
pillow==10.3.0
pins==0.8.4
pip-tools==7.4.1
pipenv==2023.7.23
platformdirs==3.10.0
plotly==5.22.0
posit-sdk==0.4.0
prometheus-client==0.19.0
prompt-toolkit==3.0.36
protobuf==4.25.3
psutil==5.9.6
psygnal==0.11.1
ptyprocess==0.7.0
pure-eval==0.2.2
pyarrow==16.1.0
pycparser==2.22
pydantic==2.7.2
pydantic_core==2.18.3
pydeck==0.9.1
Pygments==2.17.2
PyJWT==2.8.0
pyproject_hooks==1.1.0
python-dateutil==2.8.2
python-dotenv==1.0.1
python-json-logger==2.0.7
python-multipart==0.0.9
pytz==2023.3.post1
PyYAML==6.0.1
pyzmq==24.0.1
qtconsole==5.5.1
QtPy==2.4.1
questionary==2.0.1
referencing==0.32.0
requests==2.31.0
rfc3339-validator==0.1.4
rfc3986-validator==0.1.1
rich==13.7.1
rpds-py==0.13.2
scikit-learn==1.4.1.post1
scipy==1.13.1
semver==2.13.0
Send2Trash==1.8.2
shiny==1.0.0
shinyswatch==0.7.0
shinywidgets==0.3.1
six==1.16.0
smmap==5.0.1
sniffio==1.3.0
soupsieve==2.5
stack-data==0.6.3
starlette==0.27.0
streamlit==1.31.1
tabulate==0.9.0
tenacity==8.3.0
terminado==0.18.0
threadpoolctl==3.5.0
tinycss2==1.2.1
toml==0.10.2
toolz==0.12.1
tornado==6.4
traitlets==5.14.0
types-python-dateutil==2.8.19.14
typing_extensions==4.12.0
tzdata==2023.3
tzlocal==5.2
uc-micro-py==1.0.3
uri-template==1.3.0
urllib3==2.1.0
uvicorn==0.30.0
validators==0.28.3
vetiver==0.2.4
virtualenv==20.24.2
virtualenv-clone==0.5.7
watchfiles==0.22.0
wcwidth==0.2.12
webcolors==1.13
webencodings==0.5.1
websocket-client==1.7.0
websockets==12.0
Werkzeug==3.0.3
widgetsnbextension==4.0.9
xxhash==3.4.1
xyzservices==2024.4.0 zipp==3.19.1
The files, requirements.txt
and manifest.json
, are included here to simplify the tutorial. If you are creating your own extension, you can generate these files with the commmand-line interface by calling rsconnect write-manifest
or by calling rsconnect::writeManifest()
in an R console.
The extension
section of the manifest.json
file needs to be manually written to include the name
, title
, description
, and access_type
fields. The access_type
field can be set to either acl
, logged_in
, or all
.
- Create a tarball of the documents:
> tar -czvf connect-recent-users.tar.gz RecentUsers.qmd manifest.json requirements.txt
Adding the content to Connect
Visit the Connect home page.
Navigate to the Gallery page by clicking the Connect Gallery button.
Click the Deploy External Content button.
In the Deploy External Content modal, select the Bundle tab.
- If you are adding content that is publicly hosted, you can use the URL tab to add using its URL.
Drag and drop the
connect-recent-users.tar.gz
file into the drop zone or click it to select the file from your local file system.Click the Add button to upload the bundle to Connect.
The content is uploaded and published to your Connect instance. This might take several minutes.
Once published, you can Open the content in Connect.