-
Notifications
You must be signed in to change notification settings - Fork 5
Fetch Periscope data #349
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
carlospreising
wants to merge
17
commits into
MaineDSA:main
Choose a base branch
from
carlospreising:fetch-periscope
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Fetch Periscope data #349
Changes from 7 commits
Commits
Show all changes
17 commits
Select commit
Hold shift + click to select a range
21aeb1a
Allowing for scan_lists to find csv files, and updated to support the…
carlospreising 85078c7
Adding selenium script to grab newlist from periscope.
carlospreising 6b65799
adding fetch button to grab periscope data
carlospreising 9ebbcfb
adding ty to dev optional dependencies, fixing ruff and ty issues.
carlospreising 2e9808e
Ran ruff format .
carlospreising 06f04bd
removing webdrivers, using curl_cffi for requests and hangling of clo…
carlospreising db49cad
Retriggering list scan on fetching the periscope list. Cleaning up ho…
carlospreising 05db7b9
import fixes.
carlospreising e439197
Merge branch 'main' into fetch-periscope
bmos 735f1da
Update README.md
bmos 776039b
Update README.md
bmos d2c1be2
Merge branch 'main' into fetch-periscope
bmos fac02dc
update lockfile
bmos 3ab6f83
allow mixing zip and csv lists
bmos e5b9e24
use primary button style, center, add tooltip, disable if env vars ar…
bmos 3e85a3f
Merge branch 'main' into fetch-periscope
bmos 406bdbf
Update app.py
bmos File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| LIST="" #directory of your lists. "ct_membership_lists" for example | ||
| PERISCOPE_URL="" #url shared with your chapter to grab data from PERISCOPE_URL | ||
| PERISCOPE_PASS="" #password for the periscope dashboard |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,85 @@ | ||
| import html | ||
| import logging | ||
| import re | ||
| from datetime import datetime | ||
| from pathlib import Path, PurePath | ||
|
|
||
| import dotenv | ||
| from curl_cffi import requests # for dealing with cloudflare | ||
|
|
||
| logger = logging.getLogger(__name__) | ||
|
|
||
| config = dotenv.dotenv_values(Path(PurePath(__file__).parents[2], ".env")) | ||
| DOWNLOAD_DIR = config.get("LIST") or "membership_list" | ||
| PERISCOPE_URL = config.get("PERISCOPE_URL") | ||
| PERISCOPE_PASS = config.get("PERISCOPE_PASS") | ||
|
|
||
| _BASE = "https://app.periscopedata.com" | ||
| _WIDGET_TITLE = "All Members" | ||
|
|
||
|
|
||
| def _get_widget_hash(session: requests.Session, dashboard_token: str) -> str: | ||
| resp = session.get(f"{_BASE}/shared/{dashboard_token}") | ||
| resp.raise_for_status() | ||
| # widget metadata is embedded as JSON in the page HTML | ||
| text = html.unescape(resp.text) | ||
| # find the widget object by title and extract its formula_source_hash_key | ||
| # find the widget by title, then grab the formula_source_hash_key that follows it | ||
| match = re.search( | ||
| r'"title":"' + re.escape(_WIDGET_TITLE) + r'".{0,500}?"formula_source_hash_key":"([^"]+)"', | ||
| text, | ||
| re.DOTALL, | ||
| ) | ||
| if not match: | ||
| msg = f"Could not find widget '{_WIDGET_TITLE}' or its hash key in dashboard HTML." | ||
| raise LookupError(msg) | ||
| hash_key = match.group(1) | ||
| logger.info("Found widget hash: %s", hash_key) | ||
| return hash_key | ||
|
|
||
|
|
||
| def fetch_list( | ||
| download_dir: str | None = DOWNLOAD_DIR, | ||
| periscope_url: str | None = PERISCOPE_URL, | ||
| periscope_pass: str | None = PERISCOPE_PASS, | ||
| ) -> None: | ||
| if download_dir is None or periscope_url is None or periscope_pass is None: | ||
| msg = "Missing required environment variables." | ||
| raise RuntimeError(msg) | ||
|
|
||
| Path(download_dir).resolve().mkdir(parents=True, exist_ok=True) | ||
|
|
||
| session: requests.Session = requests.Session(impersonate="chrome") | ||
|
|
||
| # extract the dashboard token from the URL (last path component before any query string) | ||
| dashboard_token = periscope_url.rstrip("/").split("/")[-1] | ||
| verify_url = f"{_BASE}/shared/{dashboard_token}/verify-password" | ||
|
|
||
| # GET the password page first so Cloudflare can set challenge cookies | ||
| session.get(verify_url) | ||
|
|
||
| # POST password, requires a few other args we can leave blank | ||
| resp = session.post(verify_url, data={"password": periscope_pass, "embed": "", "border": "", "data_ts": "", "widget": ""}) | ||
| resp.raise_for_status() | ||
| logger.info("Authorized periscope.") | ||
|
|
||
| # get the widget hash from the dashboard token, which points us to the AllMembers table | ||
| widget_hash = _get_widget_hash(session, dashboard_token) | ||
|
|
||
| now = datetime.now().astimezone() | ||
| title = f"AllMembers_{now.strftime('%Y-%m-%d')}_{now.strftime('%H%M')}" # e.g. AllMembers_2-20-2026_1102 | ||
| # generate download url for the csv | ||
| download_url = f"{_BASE}/shared_dashboards/{dashboard_token}/download_csv/{widget_hash}?title={title}" | ||
|
|
||
| resp = session.get(download_url) | ||
| resp.raise_for_status() | ||
| logger.info("Downloaded CSV from periscope.") | ||
|
|
||
| dest = Path(download_dir).resolve() / f"{title}.csv" | ||
| dest.write_bytes(resp.content) | ||
| logger.info("Saved: %s", dest) | ||
|
|
||
|
|
||
| if __name__ == "__main__": | ||
| logging.basicConfig(level=logging.INFO) | ||
| fetch_list() |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.