Skip to content

Commit

Permalink
Merge pull request #133 from 444B/bug-fix
Browse files Browse the repository at this point in the history
Bug fix
  • Loading branch information
444B authored Jan 19, 2025
2 parents ebad11e + 4423c12 commit 3c8aecf
Show file tree
Hide file tree
Showing 14 changed files with 464 additions and 384 deletions.
5 changes: 4 additions & 1 deletion .flake8
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
[flake8]
ignore = E501,W503
ignore = W503,E501
per-file-ignores =
*__init__.py:F401
max-line-length = 80
86 changes: 69 additions & 17 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,9 +1,29 @@
# General access
*/api.key

# streamlit specific
*secrets.toml
config.toml
analytics.toml

# StreamlitAnalytics2 test specific
test/logs/*
test/logs
test_results_*
errors_*
test_results*.tmp

# Custom added by 444b
flake8
tree
black
test_results_*
firebase-key.json
firestore-key.json
results.json
secrets.toml
.vscode
streamlit_analytics/playground.ipynb
.key
test_results*.tmp
.devcontainer/*
analytics.toml

# Byte-compiled / optimized / DLL files
__pycache__/
Expand All @@ -27,7 +47,6 @@ parts/
sdist/
var/
wheels/
pip-wheel-metadata/
share/python-wheels/
*.egg-info/
.installed.cfg
Expand Down Expand Up @@ -57,6 +76,7 @@ coverage.xml
*.py,cover
.hypothesis/
.pytest_cache/
cover/

# Translations
*.mo
Expand All @@ -79,6 +99,7 @@ instance/
docs/_build/

# PyBuilder
.pybuilder/
target/

# Jupyter Notebook
Expand All @@ -89,16 +110,41 @@ profile_default/
ipython_config.py

# pyenv
.python-version
# For a library or package, you might want to ignore these files since the code is
# intended to run in multiple environments; otherwise, check them in:
# .python-version

# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock

# PEP 582; used by e.g. github.com/David-OConnor/pyflow
# Pipfile.lock

# UV
# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
# This is especially recommended for binary packages to ensure reproducibility, and is more
# commonly ignored for libraries.
# uv.lock

# poetry
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
# This is especially recommended for binary packages to ensure reproducibility, and is more
# commonly ignored for libraries.
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
# poetry.lock

# pdm
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
#pdm.lock
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
# in version control.
# https://pdm.fming.dev/latest/usage/project/#working-with-version-control
.pdm.toml
.pdm-python
.pdm-build/

# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
__pypackages__/

# Celery stuff
Expand Down Expand Up @@ -135,12 +181,18 @@ dmypy.json
# Pyre type checker
.pyre/

# pytype static type analyzer
.pytype/

# Custom added by 444b
flake8
tree
black
test_results_*
path/
firebase-key.json
secrets.toml
# Cython debug symbols
cython_debug/

# PyCharm
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/

# PyPI configuration file
.pypirc
24 changes: 0 additions & 24 deletions .streamlit/analytics.toml

This file was deleted.

3 changes: 1 addition & 2 deletions examples/.streamlit/analytics.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,7 @@ unsafe_password = "hunter2"
enabled = false
firestore_key_file = "firebase-key.json"
firestore_project_name = ""
firestore_collection_name = "streamlit_analytics2"
firestore_document_name = "data"
firestore_collection_name = "data"
streamlit_secrets_firestore_key = ""

[session]
Expand Down
4 changes: 3 additions & 1 deletion src/streamlit_analytics2/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,7 @@
from .main import start_tracking, stop_tracking, track # noqa: F401
from .state import data # noqa: F401

__version__ = "0.10.2"
from .state import data as counts # noqa: F401 # isort:skip

__version__ = "0.10.3"
__name__ = "streamlit_analytics2"
39 changes: 27 additions & 12 deletions src/streamlit_analytics2/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,8 @@ def load_analytics_config():

try:
if not os.path.exists(path):
# logger.warning("Configuration file not found. Creating with defaults.")
# logger.warning("Configuration file not found.
# Creating with defaults.")
ensure_streamlit_dir()
save_config(DEFAULT_CONFIG)
return DEFAULT_CONFIG.copy()
Expand All @@ -55,7 +56,8 @@ def load_analytics_config():

# Check if file is empty or missing required sections
if not config or "streamlit_analytics2" not in config:
# logger.warning("Invalid configuration found. Resetting to defaults.")
# logger.warning("Invalid configuration found.
# Resetting to defaults.")
save_config(DEFAULT_CONFIG)
return DEFAULT_CONFIG.copy()

Expand Down Expand Up @@ -91,9 +93,11 @@ def show_config():
and some that dont exist yet (like CSV)\
The Buttons do not currently do anything - please make a PR to help
implement them.\
To learn how to use all these features, please visit the [Wiki](https://github.com/444B/streamlit-analytics2/wiki) which could also do with some love
To learn how to use all these features, please visit the
[Wiki](https://github.com/444B/streamlit-analytics2/wiki)
> This will create a .streamlit/analytics.toml in the directory that you ran streamlit\
> This will create a .streamlit/analytics.toml in the directory that you
> ran `streamlit run ...`\
> You can edit the values in the text file directly if its easier
"""
Expand All @@ -104,7 +108,8 @@ def show_config():

# Configuration inputs for streamlit_analytics2
enabled = st.checkbox(
"Enable Streamlit_Analytics2", value=config["streamlit_analytics2"]["enabled"]
"Enable Streamlit_Analytics2",
value=config["streamlit_analytics2"]["enabled"],
)
st.divider()
storage_save = st.checkbox("Store Data", value=config["storage"]["save"])
Expand All @@ -114,25 +119,33 @@ def show_config():
horizontal=True,
index=0 if config["storage"]["type"] == "json" else 1,
)
save_path = st.text_input("Save File Path", value=config["storage"]["save_to_json"])
save_path = st.text_input(
"Save File Path", value=config["storage"]["save_to_json"]
) # noqa: E501
load_path = st.text_input(
"Load File Path", value=config["storage"]["load_from_json"]
)
st.divider()
verbose_logging = st.checkbox("Verbose Logging", value=config["logs"]["verbose"])
verbose_logging = st.checkbox(
"Verbose Logging", value=config["logs"]["verbose"]
) # noqa: E501
st.divider()
password = st.text_input(
"Access Password", value=config["access"]["unsafe_password"], type="password"
"Access Password",
value=config["access"]["unsafe_password"],
type="password",
)
st.divider()
firestore_enabled = st.checkbox(
"Enable Firestore", value=config["firestore"]["enabled"]
)
firestore_key_file = st.text_input(
"Firestore Key File Path", value=config["firestore"]["firestore_key_file"]
"Firestore Key File Path",
value=config["firestore"]["firestore_key_file"],
)
firestore_project = st.text_input(
"Firestore Project Name", value=config["firestore"]["firestore_project_name"]
"Firestore Project Name",
value=config["firestore"]["firestore_project_name"],
)
firestore_collection = st.text_input(
"Firestore Collection Name",
Expand All @@ -148,7 +161,9 @@ def show_config():
type="password",
)
st.divider()
session_id = st.text_input("Session ID", value=config["session"]["session_id"])
session_id = st.text_input(
"Session ID", value=config["session"]["session_id"]
) # noqa: E501
st.divider()

# Create new config from inputs
Expand All @@ -175,7 +190,7 @@ def show_config():

st.subheader("Current Configuration")
st.write(
"This is the final JSON that will get parsed to TOML in .streamlit/analytics.toml"
"This is the final JSON that will get parsed to TOML in .streamlit/analytics.toml" # noqa: E501
)
st.json(new_config)

Expand Down
23 changes: 16 additions & 7 deletions src/streamlit_analytics2/display.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,14 +53,20 @@ def show_results(data, reset_callback, unsafe_password=None): # noqa: F811
"Time spent",
utils.format_seconds(data["total_time_seconds"]),
help=(
"Time from initial page load to last widget interaction, summed over all users."
),
"Total usage from all users from run to last widget interaction"
), # noqa: E501
)
st.write("")

df = pd.DataFrame(data["per_day"])
# check if more than one year of data exists
if pd.to_datetime(df["days"]).dt.year.nunique() > 1:
x_axis_ticks = "yearmonthdate(days):O"
else:
x_axis_ticks = "monthdate(days):O"

base = alt.Chart(df).encode(
x=alt.X("monthdate(days):O", axis=alt.Axis(title="", grid=True))
x=alt.X(x_axis_ticks, axis=alt.Axis(title="", grid=True))
)
line1 = base.mark_line(point=True, stroke="#5276A7").encode(
alt.Y(
Expand Down Expand Up @@ -108,7 +114,8 @@ def show_results(data, reset_callback, unsafe_password=None): # noqa: F811
changes, not every time streamlit runs the script.</sub>
<br>
If you would like to improve the way the below metrics are
displayed, please open an issue/PR on [streamlit-analytics2](https://github.com/444B/streamlit-analytics2)
displayed, please open an issue/PR on
[streamlit-analytics2](https://github.com/444B/streamlit-analytics2)
with a clear suggestion
""",
unsafe_allow_html=True,
Expand All @@ -125,7 +132,9 @@ def show_results(data, reset_callback, unsafe_password=None): # noqa: F811
{
"widget_name": i,
"selected_value": list(data["widgets"][i].keys()),
"number_of_interactions": data["widgets"][i].values(),
"number_of_interactions": data["widgets"][
i
].values(), # noqa: E501
}
).sort_values(by="number_of_interactions", ascending=False)
)
Expand Down Expand Up @@ -155,10 +164,10 @@ def show_results(data, reset_callback, unsafe_password=None): # noqa: F811
"Continue?",
[
"No idea what I'm doing here",
"I'm absolutely sure that I want to reset the results",
"I'm sure that I want to reset the results",
],
)
if reset_prompt == "I'm absolutely sure that I want to reset the results":
if reset_prompt == "I'm sure that I want to reset the results":
reset_clicked = st.button("Click here to reset")
if reset_clicked:
reset_callback()
Expand Down
15 changes: 10 additions & 5 deletions src/streamlit_analytics2/firestore.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@
def sanitize_data(data): # noqa: F811
if isinstance(data, dict):
# Recursively sanitize dictionary keys
return {str(k) if k else "": sanitize_data(v) for k, v in data.items() if k}
return {
str(k) if k else "": sanitize_data(v) for k, v in data.items() if k
} # noqa: E501
elif isinstance(data, list):
# Apply sanitization to elements in lists
return [sanitize_data(item) for item in data]
Expand All @@ -34,7 +36,8 @@ def load(

if streamlit_secrets_firestore_key is not None:
# Following along here
# https://blog.streamlit.io/streamlit-firestore-continued/#part-4-securely-deploying-on-streamlit-sharing for deploying to Streamlit Cloud with Firestore
# https://blog.streamlit.io/streamlit-firestore-continued/#part-4-securely-deploying-on-streamlit-sharing # noqa: E501
# for deploying to Streamlit Cloud with Firestore
key_dict = json.loads(st.secrets[streamlit_secrets_firestore_key])
creds = service_account.Credentials.from_service_account_info(key_dict)
db = firestore.Client(credentials=creds, project=firestore_project_name)
Expand Down Expand Up @@ -78,7 +81,8 @@ def save(
sanitized_data = sanitize_data(data)

if streamlit_secrets_firestore_key is not None:
# Following along here https://blog.streamlit.io/streamlit-firestore-continued/#part-4-securely-deploying-on-streamlit-sharing for deploying to Streamlit Cloud with Firestore
# Following along here https://blog.streamlit.io/streamlit-firestore-continued/#part-4-securely-deploying-on-streamlit-sharing # noqa: E501
# for deploying to Streamlit Cloud with Firestore
key_dict = json.loads(st.secrets[streamlit_secrets_firestore_key])
creds = service_account.Credentials.from_service_account_info(key_dict)
db = firestore.Client(credentials=creds, project=firestore_project_name)
Expand All @@ -89,7 +93,8 @@ def save(
# currently hard coded to be "counts"

# Attempt to save to Firestore
col.document(document_name).set(sanitized_data) # creates if doesn't exist
# creates if doesn't exist
col.document(document_name).set(sanitized_data)
if session_id is not None:
sanitized_session_data = sanitize_data(ss.session_data)
col.document(session_id).set(sanitized_session_data) # creates if doesn't exist
col.document(session_id).set(sanitized_session_data)
Loading

0 comments on commit 3c8aecf

Please sign in to comment.