diff --git a/.github/workflows/sync-resume.yml b/.github/workflows/sync-resume.yml new file mode 100644 index 0000000..4025236 --- /dev/null +++ b/.github/workflows/sync-resume.yml @@ -0,0 +1,44 @@ +name: Sync Resume from Google Docs + +on: + pull_request: + branches: + - main + workflow_dispatch: + +jobs: +sync-resume: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: '3.10' + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install google-api-python-client google-auth-httplib2 google-auth-oauthlib + + - name: Run resume fetch script + env: + GOOGLE_DOC_ID: ${{ secrets.GOOGLE_DOC_ID }} + GOOGLE_CREDENTIALS: ${{ secrets.GOOGLE_CREDENTIALS }} + run: | + python scripts/main.py + + - name: Commit and push changes + run: | + git config --global user.name 'github-actions[bot]' + git config --global user.email 'github-actions[bot]@users.noreply.github.com' + git add . + if ! git diff --cached --quiet; then + git commit -m "Update resume from Google Docs" + git push origin HEAD:main + else + echo "No changes to commit" + fi diff --git a/.gitignore b/.gitignore index 0630fcb..5a40568 100644 --- a/.gitignore +++ b/.gitignore @@ -3,9 +3,8 @@ __pycache__/ *.py[cod] *$py.class -# C extensions -*.so - +.venv +.env # Distribution / packaging .Python build/ diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..efe099a --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,25 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Python: Current File", + "type": "python", + "request": "launch", + "program": "${file}", + "console": "integratedTerminal", + "justMyCode": true, + "env": {}, + "envFile": "${workspaceFolder}/.env" + }, + { + "name": "Python: main.py", + "type": "python", + "request": "launch", + "program": "${workspaceFolder}/main.py", + "console": "integratedTerminal", + "justMyCode": true, + "env": {}, + "envFile": "${workspaceFolder}/.env" + } + ] +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..0c33419 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,16 @@ +{ + "python.linting.enabled": true, + "python.linting.pylintEnabled": false, + "python.formatting.provider": "black", + "python.defaultInterpreterPath": "${workspaceFolder}/.venv/bin/python", + "[python]": { + "editor.formatOnSave": true, + "editor.codeActionsOnSave": { + "source.organizeImports": "explicit" + } + }, + "terminal.integrated.env.osx": { + "PYTHONPATH": "${workspaceFolder}", + "PYTHONUNBUFFERED": "1" + } +} diff --git a/Readme.txt b/Readme.txt deleted file mode 100644 index 6c1d343..0000000 --- a/Readme.txt +++ /dev/null @@ -1,6 +0,0 @@ -Thanks for downloading this template! - -Template Name: iPortfolio -Template URL: https://bootstrapmade.com/iportfolio-bootstrap-portfolio-websites-template/ -Author: BootstrapMade.com -License: https://bootstrapmade.com/license/ diff --git a/assets/css/style.css b/assets/css/style.css index d9522a5..3ad03fe 100644 --- a/assets/css/style.css +++ b/assets/css/style.css @@ -163,6 +163,79 @@ h1, h2, h3, h4, h5, h6 { color: #149ddd; } +/* Dropdown Menu */ +.nav-menu .dropdown { + position: relative; +} + +.nav-menu .dropdown > a { + cursor: pointer; +} + +.nav-menu .dropdown ul { + display: none; + position: absolute; + left: 0; + top: calc(100% + 15px); + z-index: 99; + width: 200px; + background: #040b14; + border: 1px solid #1a1f34; + border-radius: 5px; + padding: 10px 0; + box-shadow: 0px 10px 30px rgba(0, 0, 0, 0.5); + animation: slideDown 0.3s ease-out; +} + +@keyframes slideDown { + from { + opacity: 0; + transform: translateY(-10px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +.nav-menu .dropdown.active ul, +.nav-menu .dropdown:hover > ul { + display: block; +} + +.nav-menu .dropdown ul li { + padding: 0; + margin: 0; + border: none; +} + +.nav-menu .dropdown ul a { + padding: 10px 20px; + font-size: 14px; + display: block; + transition: 0.3s; + color: #8892b0; + border-left: 3px solid transparent; +} + +.nav-menu .dropdown ul a:hover, +.nav-menu .dropdown ul .active:hover, +.nav-menu .dropdown ul li:hover > a { + color: #149ddd; + border-left-color: #149ddd; +} + +.nav-menu .dropdown > a:after { + content: "\ea99"; + font-family: IcoFont; + padding-left: 10px; + transition: 0.3s; +} + +.nav-menu .dropdown.active > a:after { + transform: rotate(180deg); +} + /* Mobile Navigation */ .mobile-nav-toggle { position: fixed; @@ -948,9 +1021,6 @@ section { z-index: 9999; background: #040b14; } -#footer .copyright { - text-align: center; -} #footer .credits { padding-top: 5px; text-align: center; @@ -963,4 +1033,294 @@ section { width: auto; padding-right: 20px 15px; } +} + +/*-------------------------------------------------------------- +# Resume Section - One Column Layout +--------------------------------------------------------------*/ +.resume { + padding: 60px 0; + background: #f9f9f9; +} + +.resume-content { + max-width: 900px; + margin: 0 auto; +} + +/* Contact Info Box */ +.contact-info-box { + background: white; + padding: 25px; + border-radius: 5px; + box-shadow: 0 2px 8px rgba(0,0,0,0.1); + margin-bottom: 40px; + border-left: 4px solid #149ddd; +} + +.contact-info-box h4 { + color: #149ddd; + font-weight: 600; + margin-bottom: 15px; + font-size: 1.1rem; +} + +.contact-info-box p { + margin: 8px 0; + color: #555; + display: flex; + align-items: center; +} + +.contact-info-box i { + color: #149ddd; + margin-right: 10px; + font-size: 1.1rem; +} + +/* Resume Sections */ +.resume-section { + margin-bottom: 40px; +} + +.resume-title { + font-size: 1.4rem; + font-weight: 600; + margin-bottom: 25px; + color: #149ddd; + border-bottom: 3px solid #149ddd; + padding-bottom: 12px; + display: flex; + align-items: center; +} + +.resume-title i { + margin-right: 10px; + font-size: 1.3rem; +} + +/* Resume Items */ +.resume-item { + padding: 20px; + background: white; + border-left: 4px solid #149ddd; + margin-bottom: 20px; + border-radius: 0 5px 5px 0; + box-shadow: 0 2px 8px rgba(0,0,0,0.1); + transition: all 0.3s ease; +} + +.resume-item:hover { + box-shadow: 0 4px 15px rgba(20, 157, 221, 0.2); + transform: translateX(5px); +} + +.resume-item h4 { + margin: 0 0 5px 0; + color: #040b14; + font-weight: 600; + font-size: 1.1rem; +} + +.resume-item h5 { + margin: 0 0 8px 0; + font-size: 0.95rem; + color: #149ddd; + font-weight: 600; +} + +.resume-item p { + margin: 0 0 10px 0; + color: #666; + font-size: 0.95rem; +} + +.resume-item p em { + color: #888; + font-style: italic; +} + +.resume-item ul { + padding-left: 20px; + margin: 10px 0; +} + +.resume-item ul li { + margin-bottom: 8px; + color: #555; + font-size: 0.95rem; + line-height: 1.5; +} + +.resume-item strong { + color: #149ddd; + font-weight: 600; +} + +/* Skills Container */ +.skills-container { + background: white; + padding: 25px; + border-radius: 5px; + box-shadow: 0 2px 8px rgba(0,0,0,0.1); +} + +.skill-category { + margin-bottom: 25px; +} + +.skill-category:last-child { + margin-bottom: 0; +} + +.skill-category h5 { + color: #040b14; + font-weight: 600; + margin-bottom: 12px; + font-size: 1rem; + border-bottom: 1px solid #e9ecef; + padding-bottom: 8px; +} + +.skill-tags { + display: flex; + flex-wrap: wrap; + gap: 8px; +} + +.badge { + display: inline-block; + background: #f0f7ff; + color: #149ddd; + padding: 6px 12px; + border-radius: 20px; + font-size: 0.85rem; + font-weight: 500; + border: 1px solid #149ddd; + transition: all 0.3s ease; + white-space: nowrap; +} + +.badge:hover { + background: #149ddd; + color: white; + transform: translateY(-2px); + box-shadow: 0 4px 8px rgba(20, 157, 221, 0.3); +} + +/* Certification Badges */ +.cert-list p { + margin: 10px 0; +} + +.badge-cert { + display: inline-block; + background: #f0f7ff; + color: #149ddd; + padding: 8px 14px; + border-radius: 5px; + font-size: 0.95rem; + font-weight: 500; + border: 1px solid #149ddd; + transition: all 0.3s ease; +} + +.badge-cert:hover { + background: #149ddd; + color: white; +} + +/* Awards List */ +.awards-list { + list-style: none; + padding: 0; + margin: 0; +} + +.awards-list li { + padding: 12px 15px; + background: white; + margin-bottom: 10px; + border-left: 4px solid #149ddd; + border-radius: 0 3px 3px 0; + color: #555; + font-size: 0.95rem; + box-shadow: 0 2px 4px rgba(0,0,0,0.05); + transition: all 0.3s ease; +} + +.awards-list li:hover { + box-shadow: 0 4px 12px rgba(20, 157, 221, 0.15); + transform: translateX(3px); +} + +.awards-list li:before { + content: "✓ "; + color: #149ddd; + font-weight: bold; + margin-right: 8px; +} + +/* Download Button */ +.resume-download { + text-align: center; + margin-top: 50px; + padding: 30px; + background: white; + border-radius: 5px; + box-shadow: 0 2px 8px rgba(0,0,0,0.1); +} + +.btn-primary { + display: inline-block; + background: #149ddd; + border: none; + padding: 12px 30px; + font-weight: 600; + border-radius: 5px; + transition: all 0.3s ease; + color: white; + text-decoration: none; + cursor: pointer; + font-size: 1rem; +} + +.btn-primary:hover { + background: #2eafec; + transform: translateY(-2px); + box-shadow: 0 4px 15px rgba(20, 157, 221, 0.3); + color: white; + text-decoration: none; +} + +.btn-primary i { + margin-right: 8px; +} + +/* Responsive Design */ +@media (max-width: 768px) { + .resume-title { + font-size: 1.2rem; + } + + .skill-tags { + gap: 6px; + } + + .badge { + font-size: 0.8rem; + padding: 5px 10px; + } + + .resume-item { + padding: 15px; + } + + .resume-item h4 { + font-size: 1rem; + } + + .contact-info-box { + padding: 15px; + } } \ No newline at end of file diff --git a/assets/img/vb_pic.jpeg b/assets/img/vb_pic.jpeg new file mode 100644 index 0000000..5054edd Binary files /dev/null and b/assets/img/vb_pic.jpeg differ diff --git a/assets/js/main.js b/assets/js/main.js index 2a62a7d..77dbdca 100644 --- a/assets/js/main.js +++ b/assets/js/main.js @@ -4,7 +4,7 @@ * Author: BootstrapMade.com * License: https://bootstrapmade.com/license/ */ -(function() { +(function () { "use strict"; /** @@ -90,7 +90,7 @@ /** * Mobile nav toggle */ - on('click', '.mobile-nav-toggle', function(e) { + on('click', '.mobile-nav-toggle', function (e) { select('body').classList.toggle('mobile-nav-active') this.classList.toggle('bi-list') this.classList.toggle('bi-x') @@ -99,7 +99,7 @@ /** * Scrool with ofset on links with a class name .scrollto */ - on('click', '.scrollto', function(e) { + on('click', '.scrollto', function (e) { if (select(this.hash)) { e.preventDefault() @@ -149,7 +149,7 @@ new Waypoint({ element: skilsContent, offset: '80%', - handler: function(direction) { + handler: function (direction) { let progress = select('.progress .progress-bar', true); progress.forEach((el) => { el.style.width = el.getAttribute('aria-valuenow') + '%' @@ -170,9 +170,9 @@ let portfolioFilters = select('#portfolio-flters li', true); - on('click', '#portfolio-flters li', function(e) { + on('click', '#portfolio-flters li', function (e) { e.preventDefault(); - portfolioFilters.forEach(function(el) { + portfolioFilters.forEach(function (el) { el.classList.remove('filter-active'); }); this.classList.add('filter-active'); @@ -180,7 +180,7 @@ portfolioIsotope.arrange({ filter: this.getAttribute('data-filter') }); - portfolioIsotope.on('arrangeComplete', function() { + portfolioIsotope.on('arrangeComplete', function () { AOS.refresh() }); }, true); @@ -241,6 +241,36 @@ } }); + /** + * Dropdown menu toggle + */ + const dropdownLinks = select('#navbar .dropdown > a', true) + dropdownLinks.forEach(link => { + link.addEventListener('click', (e) => { + e.preventDefault() + const dropdown = link.closest('.dropdown') + dropdown.classList.toggle('active') + + // Close other dropdowns + select('#navbar .dropdown', true).forEach(d => { + if (d !== dropdown) { + d.classList.remove('active') + } + }) + }) + }) + + /** + * Close dropdown when clicking outside + */ + document.addEventListener('click', (e) => { + if (!e.target.closest('.dropdown')) { + select('#navbar .dropdown', true).forEach(d => { + d.classList.remove('active') + }) + } + }) + /** * Animation on scroll */ diff --git a/assets/resume/Natalie_Olivo_Resume.pdf b/assets/resume/Natalie_Olivo_Resume.pdf new file mode 100644 index 0000000..d5aa2ba Binary files /dev/null and b/assets/resume/Natalie_Olivo_Resume.pdf differ diff --git a/assets/vendor/php-email-form/validate.js b/assets/vendor/php-email-form/validate.js deleted file mode 100644 index e4a92d2..0000000 --- a/assets/vendor/php-email-form/validate.js +++ /dev/null @@ -1,85 +0,0 @@ -/** -* PHP Email Form Validation - v3.1 -* URL: https://bootstrapmade.com/php-email-form/ -* Author: BootstrapMade.com -*/ -(function () { - "use strict"; - - let forms = document.querySelectorAll('.php-email-form'); - - forms.forEach( function(e) { - e.addEventListener('submit', function(event) { - event.preventDefault(); - - let thisForm = this; - - let action = thisForm.getAttribute('action'); - let recaptcha = thisForm.getAttribute('data-recaptcha-site-key'); - - if( ! action ) { - displayError(thisForm, 'The form action property is not set!') - return; - } - thisForm.querySelector('.loading').classList.add('d-block'); - thisForm.querySelector('.error-message').classList.remove('d-block'); - thisForm.querySelector('.sent-message').classList.remove('d-block'); - - let formData = new FormData( thisForm ); - - if ( recaptcha ) { - if(typeof grecaptcha !== "undefined" ) { - grecaptcha.ready(function() { - try { - grecaptcha.execute(recaptcha, {action: 'php_email_form_submit'}) - .then(token => { - formData.set('recaptcha-response', token); - php_email_form_submit(thisForm, action, formData); - }) - } catch(error) { - displayError(thisForm, error) - } - }); - } else { - displayError(thisForm, 'The reCaptcha javascript API url is not loaded!') - } - } else { - php_email_form_submit(thisForm, action, formData); - } - }); - }); - - function php_email_form_submit(thisForm, action, formData) { - fetch(action, { - method: 'POST', - body: formData, - headers: {'X-Requested-With': 'XMLHttpRequest'} - }) - .then(response => { - if( response.ok ) { - return response.text() - } else { - throw new Error(`${response.status} ${response.statusText} ${response.url}`); - } - }) - .then(data => { - thisForm.querySelector('.loading').classList.remove('d-block'); - if (data.trim() == 'OK') { - thisForm.querySelector('.sent-message').classList.add('d-block'); - thisForm.reset(); - } else { - throw new Error(data ? data : 'Form submission failed and no error message returned from: ' + action); - } - }) - .catch((error) => { - displayError(thisForm, error); - }); - } - - function displayError(thisForm, error) { - thisForm.querySelector('.loading').classList.remove('d-block'); - thisForm.querySelector('.error-message').innerHTML = error; - thisForm.querySelector('.error-message').classList.add('d-block'); - } - -})(); diff --git a/assets/vendor/purecounter/purecounter.js b/assets/vendor/purecounter/purecounter.js deleted file mode 100644 index ea31fce..0000000 --- a/assets/vendor/purecounter/purecounter.js +++ /dev/null @@ -1,8 +0,0 @@ -/*! - * purecounter.js - A simple yet configurable native javascript counter which you can count on. - * Author: Stig Rex - * Version: 1.1.4 - * Url: https://github.com/srexi/purecounterjs - * License: MIT - */!function(){"use strict";function e(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function t(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function r(e,t){for(var r=0;ra.end?a.end:a.start;return n.innerHTML=r.formatNumber(i,a)}setTimeout((function(){return r.startCounter(n,a)}),a.delay)}))}},{key:"startCounter",value:function(e,t){var r=this,n=(t.end-t.start)/(t.duration/t.delay),a="inc";t.start>t.end&&(a="dec",n*=-1);var i=this.parseValue(t.start);e.innerHTML=this.formatNumber(i,t),!0===t.once&&e.setAttribute("data-purecounter-duration",0);var o=setInterval((function(){var s=r.nextNumber(i,n,a);e.innerHTML=r.formatNumber(s,t),((i=s)>=t.end&&"inc"==a||i<=t.end&&"dec"==a)&&(e.innerHTML=r.formatNumber(t.end,t),clearInterval(o))}),t.delay)}},{key:"parseConfig",value:function(r){var n=this,a=function(r){for(var n=1;n2&&void 0!==arguments[2]?arguments[2]:"inc";return e=this.parseValue(e),t=this.parseValue(t),parseFloat("inc"===r?e+t:e-t)}},{key:"convertToCurrencySystem",value:function(e,t){var r=t.currencysymbol||"",n=t.decimals||1;return r+((e=Math.abs(Number(e)))>=1e12?"".concat((e/1e12).toFixed(n)," T"):e>=1e9?"".concat((e/1e9).toFixed(n)," B"):e>=1e6?"".concat((e/1e6).toFixed(n)," M"):e>=1e3?"".concat((e/1e12).toFixed(n)," K"):e.toFixed(n))}},{key:"applySeparator",value:function(e,t){return t.separator?e.replace(/(\d)(?=(\d{3})+(?!\d))/g,"$1,").replace(new RegExp(/,/gi,"gi"),t.separatorsymbol):e.replace(new RegExp(/,/gi,"gi"),"")}},{key:"formatNumber",value:function(e,t){var r={minimumFractionDigits:t.decimals,maximumFractionDigits:t.decimals};return e=t.currency?this.convertToCurrencySystem(e,t):parseFloat(e),this.applySeparator(e.toLocaleString(void 0,r),t)}},{key:"parseValue",value:function(e){return/^[0-9]+\.[0-9]+$/.test(e)?parseFloat(e):/^[0-9]+$/.test(e)?parseInt(e):/^true|false/i.test(e)?/^true/i.test(e):e}},{key:"elementIsInView",value:function(e){for(var t=e.offsetTop,r=e.offsetLeft,n=e.offsetWidth,a=e.offsetHeight;e.offsetParent;)t+=(e=e.offsetParent).offsetTop,r+=e.offsetLeft;return t>=window.pageYOffset&&r>=window.pageXOffset&&t+a<=window.pageYOffset+window.innerHeight&&r+n<=window.pageXOffset+window.innerWidth}},{key:"intersectionListenerSupported",value:function(){return"IntersectionObserver"in window&&"IntersectionObserverEntry"in window&&"intersectionRatio"in window.IntersectionObserverEntry.prototype}}])&&r(a.prototype,i),o&&r(a,o),n}())}(); -//# sourceMappingURL=purecounter.js.map \ No newline at end of file diff --git a/forms/Readme.txt b/forms/Readme.txt deleted file mode 100644 index c758c4e..0000000 --- a/forms/Readme.txt +++ /dev/null @@ -1,2 +0,0 @@ -Fully working PHP/AJAX contact form script is available in the pro version of the template. -You can buy it from: https://bootstrapmade.com/iportfolio-bootstrap-portfolio-websites-template/ diff --git a/forms/contact.php b/forms/contact.php deleted file mode 100644 index 8476ba4..0000000 --- a/forms/contact.php +++ /dev/null @@ -1,41 +0,0 @@ -ajax = true; - - $contact->to = $receiving_email_address; - $contact->from_name = $_POST['name']; - $contact->from_email = $_POST['email']; - $contact->subject = $_POST['subject']; - - // Uncomment below code if you want to use SMTP to send emails. You need to enter your correct SMTP credentials - /* - $contact->smtp = array( - 'host' => 'example.com', - 'username' => 'example', - 'password' => 'pass', - 'port' => '587' - ); - */ - - $contact->add_message( $_POST['name'], 'From'); - $contact->add_message( $_POST['email'], 'Email'); - $contact->add_message( $_POST['message'], 'Message', 10); - - echo $contact->send(); -?> diff --git a/index.html b/index.html index cc85975..a936a2a 100644 --- a/index.html +++ b/index.html @@ -48,7 +48,6 @@

Natalie Olivo

@@ -58,8 +57,19 @@

Natalie Olivo

@@ -69,7 +79,7 @@

Natalie Olivo

Natalie Olivo

-

Resume/About/Projects/Blog

+

About/Resume/Blog

@@ -86,8 +96,7 @@

About

- Life without graph databases. -
Charlie Day, "Always Sunny in Philadelphia", passionately performing Extract, Transform, and Load processes.
+ Life without graph databases.

Natalie Olivo, Machine Learning Engineer

@@ -101,129 +110,331 @@

Natalie Olivo, Machine Learning Engineer

-
-
-

Community

-

- I've lived in Fort Wayne, Chicago, Louisville, Boston and DC for career opportunities and found myself naturally embedded in professional communities. - Here in Cleveland, I electively seek this community out and help create it as Director of Women Who Code. We organize monthly virtual events aimed to highlight, connect, and empower technologists in the Cleveland area and beyond. -

-

- In my first job in my career as an actuarial analyst, I had a front-row seat to the transition from Pensions to 401ks. - In my first job in my next career as a data scientist, I had a front-row seat to the increasing privitization of healthcare related research in the US. - After the outbreak of COVID-19 and the wide-spread adoption of remote work in 2020, I reflected on my priorities and found myself leaving Washington, DC for Cleveland, OH so that I may be closer to my family, and many incredible volleyball venues. -

-

- Volleyball and the community it brings me are really important parts of my life. I joyously play at the AVP BB level. -

-
- + + + +
+
+
+

Resume

+

+
+ +
+ + + + + +
+

Professional Summary

+

AI/ML Engineer with 8+ years of experience designing and deploying scalable machine learning systems, data pipelines, and AI-powered solutions across government, insurance, and supply chain sectors. Proven track record of architecting enterprise-scale ML frameworks, building agentic AI systems, and leading cross-functional technical initiatives.

+
+ + +
+

Core Technical Skills

+
+
+
Machine Learning & AI
+
+ PyTorch + Huggingface + RLHF + MCP + Agent Development + Supervised Fine Tuning + Random Forest + Feature Engineering + EDA +
-
-
-
- Badges of Flair -
Joanna, "Office Space" (1999) doesn't like talking about her flair.
+ +
+
Cloud & Infrastructure
+
+ AWS (Textract, GroundTruth, Batch, S3, Athena) + Azure (AI Foundry, Functions, DevOps) + Docker + Terraform + Microservices + Event-Driven Architecture + Kubernetes +
-
-

Certifications

-

-

-

+ +
+
Data Engineering
+
+ PySpark + SQL + Relational Databases + Data Pipelines + ETL/ELT +
-
- - +
+
+ + +
+

Professional Experience

+ +
+

Artificial Intelligence Engineer

+
Omada | Nov 2024 - Present
+

Alicante, Valencian Community, Spain · Hybrid

+
    +
  • Scaled an event-driven service from experimental collaborative filtering prototype, exposing anomaly detection capabilities as RESTful tools for LLM chatbot agent
  • +
  • Collaborated with SMEs to generalize anomaly scoring for diverse organizational models and implemented configurable hierarchical scoring separating data-driven clustering from customer-defined groupings, maintaining mathematical validity through proper vector normalization
  • +
  • Skills: Mathematical Programming · Agentic Architecture · AI Product Development · sklearn
  • +
+
+ +
+

Data Engineer Lead

+
Progressive Insurance | Jan 2024 - Jul 2024 (7 mos)
+

Cleveland, Ohio, United States · Remote

+
    +
  • Architected service orchestration platform implementing patterns that later formalized as MCP and LangGraph: built standardized interface layer coordinating legacy and novel services (LLM APIs, template systems, feedback collection, data storage) through plugin architecture, composition workflows, and interchangeable components
  • +
  • Served 22K users with production reliability while enabling researchers to swap LLM providers and prompts via configuration, reducing iteration cycles from months to days
  • +
  • Skills: Microservices · AI Software Development · API Development
  • +
+
+ +
+

Data Science Consultant - Top Secret Clearance

+
Various | Apr 2023 - Jan 2024 (10 mos)
+

Cleveland, Ohio, United States · Remote

+
    +
  • Supplemented existing image segmentation pipeline with jurisdiction classification by first extracting plate features scraped from public sources (scrapy), indexing those features, and performing nearest neighbor search - similar to RAG but for images
  • +
  • Leveraged existing tools to create drift detection tool, which established benchmarks and alternative processing for non-ideal image-capture conditions
  • +
  • Delivered rapid prototyping and repeatable patterns with customized CI/CD pipelines
  • +
  • Skills: PyTorch · Edge Deployment · ONNX · Supervised Fine Tuning · Strategy · Software Deployment
  • +
+
+
+

Machine Learning Engineer II

+
Interos Inc | Jan 2022 - Oct 2022 (10 mos)
+

Arlington, Virginia, United States

+
    +
  • Architected and scaled an orchestrator coordinating ML models, human validation, and knowledge graph ingestion
  • +
  • Built information parsing framework for multi-modal document processing (PDFs, images, tables), implementing entity matching, data normalization, and deduplication across diverse document formats and sources
  • +
  • Crafted features which supported re-training of models, research tooling, and governance using version control, CI/CD pipelines, and thoughtful database schema and API design
  • +
  • Clients: NASA, DOD, Mastercard, MassMutual, Delta
  • +
  • Skills: RLHF · Event-Driven Architecture · Huggingface · AWS Textract · AWS GroundTruth · PySpark · CI/CD · Kubernetes
  • +
+
+ +
+

Machine Learning Engineer

+
Interos Inc | Oct 2019 - Jan 2022 (2 yrs 4 mos)
+

Arlington, Virginia

+
    +
  • Shipped DAGs that automate key information discovery: scraping company websites, routing proposals to Human-in-the-Loop validation, and storing verified relationships as structured data
  • +
  • Created a rigorous unit-testing framework enabling reliable simulation of external dependencies and catching issues previously only observed in production
  • +
  • Led a monthly ML research reading group focused on emerging methods and foundational papers
  • +
  • Skills: FastAPI · EDA · Docker · pytest · Prefect Dataflow Automation
  • +
+
+ +
+

Machine Learning Engineer

+
SoKat.co / Dovel Technologies | Nov 2018 - Sep 2019 (11 mos)
+

Washington D.C. Metro Area

+
    +
  • Contractor for Grant Solutions of The Department of Health and Human Services to create and support 2-time Government Innovation Award winning product Recipient Insight
  • +
  • Engineered a distributed PySpark pipeline on EMR with AWS Batch orchestration, an internally managed precursor to what AWS Glue later standardized
  • +
  • Created a TF-IDF-powered entity-resolution tool used to match DUNS and EIN identifiers to organization name and address
  • +
  • Skills: SQL · Feature Engineering · Random Forest · AWS Batch · PySpark · Tableau · Terraform
  • +
+
-
- -
-
+ +
- -
-
+ +
+

Awards & Recognition

+
    +
  • Supported 2-time Government Innovation Award winning product (Recipient Insight)
  • +
  • Won blogging competition at General Assembly for research on Effectiveness of Humanitarian Aid
  • +
  • Interos Virtual Talent Show Winner - Live piano and vocal performance
  • +
+
-
-

Resume

-

+ +
+

Early Career Experience

+ +
+

Senior Vendor/Industrial Base Analyst

+
Pantheon Integrated Solutions, Inc. | Apr 2018 - Oct 2018 (7 mos)
+

Washington DC-Baltimore Area

+
    +
  • Applied data science solutions and strategies to the Department of Navy's VIRGINIA Class Program Office
  • +
  • Established and incorporated machine learning capabilities to enhance program management support
  • +
  • Created data pipelines for labor hour and material cost data from prime shipbuilders and sub-tier vendors
  • +
-
- + +
+

Actuarial Contractor

+
Lynchval Systems Worldwide Inc | Mar 2017 - Mar 2018 (1 yr 1 mo)
+

The Pension Benefits Guarantee Corporation (PBGC)

+
    +
  • Delivered an annual data scraping, collecting, and validation process for Pension Insurance performance projections
  • +
  • Utilized the Department of Labor Form 5500 website, Visual Basic Macros, MS Excel to create a database for 450 Single-employer plans and 250 Multi-employer plans
  • +
  • Automated checking and reasonability processes by writing Visual Basic macros
  • +
+
+ +
+

Actuarial Analyst

+
Willis Towers Watson | Jul 2013 - Jun 2016 (3 yrs)
+

Greater Boston

+
    +
  • Assisted in bulk lump sum offerings and accrued benefit computation projects
  • +
  • Completed pension valuation work per PPA, PBGC and ASC guidelines
  • +
  • Simplified reporting processes by automating and maintaining client specific spreadsheets
  • +
+
+

Actuarial Intern

+
Mercer | Jun 2012 - Aug 2012 (3 mos)
+

Louisville, Kentucky

+
    +
  • Specialized in the completion of annual IRS government forms for qualified retirement plans
  • +
  • Assisted in producing actuarial valuation reports for defined contribution pension and post-retiree medical plans
  • +
  • Generated and reviewed employee-specific pension calculations with proprietary software and plan documents
  • +
  • Skills: Employee Benefits · Data Cleaning · Mathematics · Statistical Analysis · EDA · Exploratory Data Analysis · Mathematical Programming · Data Analytics · Relational Databases
  • +
+
+ +
+

Actuarial Intern

+
CNA Insurance | Jun 2011 - Aug 2011 (3 mos)
+

Chicago, Illinois

+
    +
  • Contributed to an ongoing Reserve Review for Professional Liability automotive, worker's compensation, property and general liability lines of insurance
  • +
  • Analyzed past claims data to develop and justify loss development factors used to project future losses
  • +
  • Collaborated with internal business divisions to compare different loss reserve development methods
  • +
  • Skills: SQL · Employee Benefits · Data Cleaning · Mathematics · Statistical Analysis · EDA · Data Analytics · Relational Databases
  • +
+
+ +
+

Actuarial Intern

+
Swiss Re | May 2010 - Aug 2010 (4 mos)
+

Fort Wayne, Indiana

+
    +
  • Evaluated Life and Health reinsurance pricing with industry specific software
  • +
  • Attended seminars and completed assignments over different aspects of actuarial work in Life and Health Reinsurance
  • +
  • Skills: Employee Benefits · Data Cleaning · Mathematics · Statistical Analysis · EDA · Data Analytics · Relational Databases
  • +
+
-
+ +
+ + +
-
diff --git a/inner-page.html b/inner-page.html deleted file mode 100644 index a4b4f7b..0000000 --- a/inner-page.html +++ /dev/null @@ -1,133 +0,0 @@ - - - - - - - - Inner Page - iPortfolio Bootstrap Template - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - -
-
-

- Example inner page template -

-
-
- -
- - -
-
- -
- - - - - Designed by BootstrapMade -
-
-
- - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/main.py b/main.py new file mode 100644 index 0000000..0d4d37d --- /dev/null +++ b/main.py @@ -0,0 +1,291 @@ +import json +import os +import re +from datetime import datetime +from urllib.request import urlopen + +from google.auth.transport.requests import Request +from google.oauth2 import service_account +from googleapiclient.discovery import build + + +def fetch_google_doc(doc_id, creds): + """Fetch Google Doc content using Docs API""" + service = build('docs', 'v1', credentials=creds) + doc = service.documents().get(documentId=doc_id).execute() + return doc + + +def extract_text_from_doc(doc_content): + """Extract all text and structure from Google Doc, tracking bold formatting""" + sections = {} + current_section = None + current_list = [] + + for element in doc_content.get('body', {}).get('content', []): + if 'paragraph' in element: + paragraph = element.get('paragraph', {}) + + # Get all text and track if first run is bold + text_parts = [] + first_run_bold = False + + for idx, run in enumerate(paragraph.get('elements', [])): + if 'textRun' in run: + text = run['textRun'].get('content', '') + text_style = run['textRun'].get('textStyle', {}) + is_bold = text_style.get('bold', False) + + # Track if first run (that has content) is bold + if idx == 0 and text.strip() and is_bold: + first_run_bold = True + + text_parts.append(text) + + text = ''.join(text_parts).strip() + + if not text: + if current_list and current_section: + sections[current_section] = current_list + current_list = [] + continue + + # Detect heading levels + style = paragraph.get('paragraphStyle', {}) + named_style = style.get('namedStyleType', '') + + if named_style == 'HEADING_1' or named_style == 'HEADING_2': + if current_list and current_section: + sections[current_section] = current_list + current_section = text + current_list = [] + else: + # Check if it's a bullet point + if paragraph.get('bullet'): + current_list.append(text) + # Check if it's a bold label like "Skills:", "Clients:", "Philosophy:" + elif first_run_bold and (':' in text or ';' in text): + # This is a bold label, add it as a special marker + current_list.append(text) + else: + if current_section and current_list: + sections[current_section] = current_list + current_list = [] + sections[text] = None + + if current_list and current_section: + sections[current_section] = current_list + + return sections + + +def download_pdf(doc_id, creds, output_path): + """Download Google Doc as PDF""" + try: + drive_service = build('drive', 'v3', credentials=creds) + request = drive_service.files().export_media( + fileId=doc_id, + mimeType='application/pdf' + ) + pdf_content = request.execute() + + # Save PDF + os.makedirs(os.path.dirname(output_path), exist_ok=True) + with open(output_path, 'wb') as f: + f.write(pdf_content) + print(f"✓ PDF saved to {output_path}") + return True + except Exception as e: + print(f"✗ Error downloading PDF: {e}") + return False + + +def generate_resume_html(sections, include_contact=False): + """Generate resume HTML only for Professional Experience and beyond""" + html = [] + + # Sections to update (skip Professional Summary and Core Technical Skills) + sections_to_update = [ + 'Professional Experience', + 'Projects', + 'Certifications', + 'Education & Training', + 'Awards & Recognition', + 'Early Career Experience', + ] + + section_mapping = { + 'Professional Experience': ('briefcase', 'Professional Experience'), + 'Projects': ('code-square', 'Featured Projects'), + 'Certifications': ('award', 'Certifications'), + 'Education & Training': ('book', 'Education & Training'), + 'Awards & Recognition': ('star', 'Awards & Recognition'), + 'Early Career Experience': ('clock-history', 'Early Career Experience'), + } + + for section_name in sections_to_update: + if section_name not in sections: + continue + + icon, display_name = section_mapping[section_name] + content = sections[section_name] + + html.append(f' ') + html.append('
') + html.append( + f'

{display_name}

') + + # Awards - simple list + if section_name == 'Awards & Recognition': + html.append('
    ') + if isinstance(content, list): + for item in content: + html.append(f'
  • {item}
  • ') + html.append('
') + + # Certifications - badge list + elif section_name == 'Certifications': + html.append('
') + if isinstance(content, list): + for item in content: + html.append( + f'

{item}

') + html.append('
') + + # Experience & Education - items with title, company/dates, location, bullets + else: + if isinstance(content, list): + i = 0 + while i < len(content): + item = content[i] + + # Skip empty lines + if not item or item.strip() == '': + i += 1 + continue + + html.append('
') + html.append(f'

{item}

') + i += 1 + + # Next line: Company | Dates (company name and full time on same line) + if i < len(content) and content[i].strip(): + html.append(f'
{content[i]}
') + i += 1 + + # Next line: Location (in italics) + if i < len(content) and content[i].strip(): + html.append(f'

{content[i]}

') + i += 1 + + # Collect bullet points and bold labels until next title or end + bullets = [] + while i < len(content): + line = content[i].strip() + # Stop if we hit an empty line followed by what looks like a title + if not line: + i += 1 + # Check if next non-empty line is a title (starts with capital, followed by role words) + j = i + while j < len(content) and not content[j].strip(): + j += 1 + if j < len(content) and content[j][0].isupper() and not any(content[j].startswith(label) for label in ['Skills:', 'Clients:', 'Philosophy:']): + break + continue + # Check if it's a bold label (Skills:, Clients:, Philosophy:, etc.) + is_bold_label = any(line.startswith(label) for label in [ + 'Skills:', 'Clients:', 'Philosophy:', 'Technologies:']) + # If it's a bullet point, bold label, or regular description, add it + if line.startswith('•') or line.startswith('-') or is_bold_label or not line[0].isupper(): + # Clean bullet markers + if line.startswith('•') or line.startswith('-'): + line = line[1:].strip() + bullets.append(line) + i += 1 + else: + # Stop at next title + break + + if bullets: + html.append('
    ') + for bullet in bullets: + # Check if bullet starts with bold labels like "Skills:", "Clients:", "Philosophy:", etc. + if ':' in bullet: + potential_label = bullet.split(':')[0].strip() + if potential_label in ['Skills', 'Clients', 'Philosophy', 'Technologies']: + label, content_part = bullet.split(':', 1) + html.append( + f'
  • {label}:{content_part}
  • ') + else: + html.append( + f'
  • {bullet}
  • ') + else: + html.append(f'
  • {bullet}
  • ') + html.append('
') + + html.append('
') + + html.append('
') + + return '\n'.join(html) + + +def update_index_html(resume_html): + """Update the resume section in index.html""" + with open('index.html', 'r', encoding='utf-8') as f: + content = f.read() + + # Find and replace resume section + pattern = r'
.*?
\s*' + replacement = f'
\n{resume_html}\n
\n ' + + updated_content = re.sub(pattern, replacement, content, flags=re.DOTALL) + + with open('index.html', 'w', encoding='utf-8') as f: + f.write(updated_content) + + print("✓ index.html updated with resume content") + + +def main(): + # Load credentials from environment + creds_json = os.environ.get('GOOGLE_CREDENTIALS') + if not creds_json: + print("Error: GOOGLE_CREDENTIALS environment variable not set") + return + + creds_info = json.loads(creds_json) + creds = service_account.Credentials.from_service_account_info(creds_info, scopes=[ + 'https://www.googleapis.com/auth/documents.readonly', + 'https://www.googleapis.com/auth/drive.readonly' # Add this! + ]) + + doc_id = os.environ.get('GOOGLE_DOC_ID') + if not doc_id: + print("Error: GOOGLE_DOC_ID environment variable not set") + return + + print("Fetching Google Doc...") + doc = fetch_google_doc(doc_id, creds) + + print("Extracting resume data...") + sections = extract_text_from_doc(doc) + + print("Downloading PDF...") + pdf_path = 'assets/resume/Natalie_Olivo_Resume.pdf' + download_pdf(doc_id, creds, pdf_path) + + # print("Generating HTML...") + # resume_html = generate_resume_html(sections, include_contact=False) + + # print("Updating index.html...") + # update_index_html(resume_html) + + print("\n✓ Resume updated successfully!") + # print(f" - HTML updated in index.html") + print(f" - PDF saved to {pdf_path}") + # print(f" - Phone/Email hidden from website display") + + +if __name__ == '__main__': + main() diff --git a/portfolio-details.html b/portfolio-details.html deleted file mode 100644 index 17967e4..0000000 --- a/portfolio-details.html +++ /dev/null @@ -1,175 +0,0 @@ - - - - - - - - Portfolio Details - iPortfolio Bootstrap Template - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - -
-
- -
- -
-
-
- -
- -
- -
- -
- -
- -
- -
-
-
-
- -
-
-

Project information

-
    -
  • Category: Web design
  • -
  • Client: ASU Company
  • -
  • Project date: 01 March, 2020
  • -
  • Project URL: www.example.com
  • -
-
-
-

This is an example of portfolio detail

-

- Autem ipsum nam porro corporis rerum. Quis eos dolorem eos itaque inventore commodi labore quia quia. Exercitationem repudiandae officiis neque suscipit non officia eaque itaque enim. Voluptatem officia accusantium nesciunt est omnis tempora consectetur dignissimos. Sequi nulla at esse enim cum deserunt eius. -

-
-
- -
- -
-
- -
- - -
-
- -
- - - - - Designed by BootstrapMade -
-
-
- - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..0705149 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,10 @@ +[project] +name = "portfolio" +version = "0.1.0" +description = "Personal portfolio website" +readme = "README.md" +requires-python = ">=3.11" +dependencies = [ + "google-api-python-client>=2.187.0", + "google-auth-oauthlib>=1.2.3", +] diff --git a/uv.lock b/uv.lock new file mode 100644 index 0000000..a24a71f --- /dev/null +++ b/uv.lock @@ -0,0 +1,358 @@ +version = 1 +revision = 3 +requires-python = ">=3.10" +resolution-markers = [ + "python_full_version >= '3.13'", + "python_full_version < '3.13'", +] + +[[package]] +name = "cachetools" +version = "6.2.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/bc/1d/ede8680603f6016887c062a2cf4fc8fdba905866a3ab8831aa8aa651320c/cachetools-6.2.4.tar.gz", hash = "sha256:82c5c05585e70b6ba2d3ae09ea60b79548872185d2f24ae1f2709d37299fd607", size = 31731, upload-time = "2025-12-15T18:24:53.744Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2c/fc/1d7b80d0eb7b714984ce40efc78859c022cd930e402f599d8ca9e39c78a4/cachetools-6.2.4-py3-none-any.whl", hash = "sha256:69a7a52634fed8b8bf6e24a050fb60bff1c9bd8f6d24572b99c32d4e71e62a51", size = 11551, upload-time = "2025-12-15T18:24:52.332Z" }, +] + +[[package]] +name = "certifi" +version = "2025.11.12" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a2/8c/58f469717fa48465e4a50c014a0400602d3c437d7c0c468e17ada824da3a/certifi-2025.11.12.tar.gz", hash = "sha256:d8ab5478f2ecd78af242878415affce761ca6bc54a22a27e026d7c25357c3316", size = 160538, upload-time = "2025-11-12T02:54:51.517Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/70/7d/9bc192684cea499815ff478dfcdc13835ddf401365057044fb721ec6bddb/certifi-2025.11.12-py3-none-any.whl", hash = "sha256:97de8790030bbd5c2d96b7ec782fc2f7820ef8dba6db909ccf95449f2d062d4b", size = 159438, upload-time = "2025-11-12T02:54:49.735Z" }, +] + +[[package]] +name = "charset-normalizer" +version = "3.4.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/13/69/33ddede1939fdd074bce5434295f38fae7136463422fe4fd3e0e89b98062/charset_normalizer-3.4.4.tar.gz", hash = "sha256:94537985111c35f28720e43603b8e7b43a6ecfb2ce1d3058bbe955b73404e21a", size = 129418, upload-time = "2025-10-14T04:42:32.879Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1f/b8/6d51fc1d52cbd52cd4ccedd5b5b2f0f6a11bbf6765c782298b0f3e808541/charset_normalizer-3.4.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e824f1492727fa856dd6eda4f7cee25f8518a12f3c4a56a74e8095695089cf6d", size = 209709, upload-time = "2025-10-14T04:40:11.385Z" }, + { url = "https://files.pythonhosted.org/packages/5c/af/1f9d7f7faafe2ddfb6f72a2e07a548a629c61ad510fe60f9630309908fef/charset_normalizer-3.4.4-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4bd5d4137d500351a30687c2d3971758aac9a19208fc110ccb9d7188fbe709e8", size = 148814, upload-time = "2025-10-14T04:40:13.135Z" }, + { url = "https://files.pythonhosted.org/packages/79/3d/f2e3ac2bbc056ca0c204298ea4e3d9db9b4afe437812638759db2c976b5f/charset_normalizer-3.4.4-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:027f6de494925c0ab2a55eab46ae5129951638a49a34d87f4c3eda90f696b4ad", size = 144467, upload-time = "2025-10-14T04:40:14.728Z" }, + { url = "https://files.pythonhosted.org/packages/ec/85/1bf997003815e60d57de7bd972c57dc6950446a3e4ccac43bc3070721856/charset_normalizer-3.4.4-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f820802628d2694cb7e56db99213f930856014862f3fd943d290ea8438d07ca8", size = 162280, upload-time = "2025-10-14T04:40:16.14Z" }, + { url = "https://files.pythonhosted.org/packages/3e/8e/6aa1952f56b192f54921c436b87f2aaf7c7a7c3d0d1a765547d64fd83c13/charset_normalizer-3.4.4-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:798d75d81754988d2565bff1b97ba5a44411867c0cf32b77a7e8f8d84796b10d", size = 159454, upload-time = "2025-10-14T04:40:17.567Z" }, + { url = "https://files.pythonhosted.org/packages/36/3b/60cbd1f8e93aa25d1c669c649b7a655b0b5fb4c571858910ea9332678558/charset_normalizer-3.4.4-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9d1bb833febdff5c8927f922386db610b49db6e0d4f4ee29601d71e7c2694313", size = 153609, upload-time = "2025-10-14T04:40:19.08Z" }, + { url = "https://files.pythonhosted.org/packages/64/91/6a13396948b8fd3c4b4fd5bc74d045f5637d78c9675585e8e9fbe5636554/charset_normalizer-3.4.4-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:9cd98cdc06614a2f768d2b7286d66805f94c48cde050acdbbb7db2600ab3197e", size = 151849, upload-time = "2025-10-14T04:40:20.607Z" }, + { url = "https://files.pythonhosted.org/packages/b7/7a/59482e28b9981d105691e968c544cc0df3b7d6133152fb3dcdc8f135da7a/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:077fbb858e903c73f6c9db43374fd213b0b6a778106bc7032446a8e8b5b38b93", size = 151586, upload-time = "2025-10-14T04:40:21.719Z" }, + { url = "https://files.pythonhosted.org/packages/92/59/f64ef6a1c4bdd2baf892b04cd78792ed8684fbc48d4c2afe467d96b4df57/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:244bfb999c71b35de57821b8ea746b24e863398194a4014e4c76adc2bbdfeff0", size = 145290, upload-time = "2025-10-14T04:40:23.069Z" }, + { url = "https://files.pythonhosted.org/packages/6b/63/3bf9f279ddfa641ffa1962b0db6a57a9c294361cc2f5fcac997049a00e9c/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:64b55f9dce520635f018f907ff1b0df1fdc31f2795a922fb49dd14fbcdf48c84", size = 163663, upload-time = "2025-10-14T04:40:24.17Z" }, + { url = "https://files.pythonhosted.org/packages/ed/09/c9e38fc8fa9e0849b172b581fd9803bdf6e694041127933934184e19f8c3/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:faa3a41b2b66b6e50f84ae4a68c64fcd0c44355741c6374813a800cd6695db9e", size = 151964, upload-time = "2025-10-14T04:40:25.368Z" }, + { url = "https://files.pythonhosted.org/packages/d2/d1/d28b747e512d0da79d8b6a1ac18b7ab2ecfd81b2944c4c710e166d8dd09c/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:6515f3182dbe4ea06ced2d9e8666d97b46ef4c75e326b79bb624110f122551db", size = 161064, upload-time = "2025-10-14T04:40:26.806Z" }, + { url = "https://files.pythonhosted.org/packages/bb/9a/31d62b611d901c3b9e5500c36aab0ff5eb442043fb3a1c254200d3d397d9/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:cc00f04ed596e9dc0da42ed17ac5e596c6ccba999ba6bd92b0e0aef2f170f2d6", size = 155015, upload-time = "2025-10-14T04:40:28.284Z" }, + { url = "https://files.pythonhosted.org/packages/1f/f3/107e008fa2bff0c8b9319584174418e5e5285fef32f79d8ee6a430d0039c/charset_normalizer-3.4.4-cp310-cp310-win32.whl", hash = "sha256:f34be2938726fc13801220747472850852fe6b1ea75869a048d6f896838c896f", size = 99792, upload-time = "2025-10-14T04:40:29.613Z" }, + { url = "https://files.pythonhosted.org/packages/eb/66/e396e8a408843337d7315bab30dbf106c38966f1819f123257f5520f8a96/charset_normalizer-3.4.4-cp310-cp310-win_amd64.whl", hash = "sha256:a61900df84c667873b292c3de315a786dd8dac506704dea57bc957bd31e22c7d", size = 107198, upload-time = "2025-10-14T04:40:30.644Z" }, + { url = "https://files.pythonhosted.org/packages/b5/58/01b4f815bf0312704c267f2ccb6e5d42bcc7752340cd487bc9f8c3710597/charset_normalizer-3.4.4-cp310-cp310-win_arm64.whl", hash = "sha256:cead0978fc57397645f12578bfd2d5ea9138ea0fac82b2f63f7f7c6877986a69", size = 100262, upload-time = "2025-10-14T04:40:32.108Z" }, + { url = "https://files.pythonhosted.org/packages/ed/27/c6491ff4954e58a10f69ad90aca8a1b6fe9c5d3c6f380907af3c37435b59/charset_normalizer-3.4.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:6e1fcf0720908f200cd21aa4e6750a48ff6ce4afe7ff5a79a90d5ed8a08296f8", size = 206988, upload-time = "2025-10-14T04:40:33.79Z" }, + { url = "https://files.pythonhosted.org/packages/94/59/2e87300fe67ab820b5428580a53cad894272dbb97f38a7a814a2a1ac1011/charset_normalizer-3.4.4-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5f819d5fe9234f9f82d75bdfa9aef3a3d72c4d24a6e57aeaebba32a704553aa0", size = 147324, upload-time = "2025-10-14T04:40:34.961Z" }, + { url = "https://files.pythonhosted.org/packages/07/fb/0cf61dc84b2b088391830f6274cb57c82e4da8bbc2efeac8c025edb88772/charset_normalizer-3.4.4-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:a59cb51917aa591b1c4e6a43c132f0cdc3c76dbad6155df4e28ee626cc77a0a3", size = 142742, upload-time = "2025-10-14T04:40:36.105Z" }, + { url = "https://files.pythonhosted.org/packages/62/8b/171935adf2312cd745d290ed93cf16cf0dfe320863ab7cbeeae1dcd6535f/charset_normalizer-3.4.4-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8ef3c867360f88ac904fd3f5e1f902f13307af9052646963ee08ff4f131adafc", size = 160863, upload-time = "2025-10-14T04:40:37.188Z" }, + { url = "https://files.pythonhosted.org/packages/09/73/ad875b192bda14f2173bfc1bc9a55e009808484a4b256748d931b6948442/charset_normalizer-3.4.4-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d9e45d7faa48ee908174d8fe84854479ef838fc6a705c9315372eacbc2f02897", size = 157837, upload-time = "2025-10-14T04:40:38.435Z" }, + { url = "https://files.pythonhosted.org/packages/6d/fc/de9cce525b2c5b94b47c70a4b4fb19f871b24995c728e957ee68ab1671ea/charset_normalizer-3.4.4-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:840c25fb618a231545cbab0564a799f101b63b9901f2569faecd6b222ac72381", size = 151550, upload-time = "2025-10-14T04:40:40.053Z" }, + { url = "https://files.pythonhosted.org/packages/55/c2/43edd615fdfba8c6f2dfbd459b25a6b3b551f24ea21981e23fb768503ce1/charset_normalizer-3.4.4-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ca5862d5b3928c4940729dacc329aa9102900382fea192fc5e52eb69d6093815", size = 149162, upload-time = "2025-10-14T04:40:41.163Z" }, + { url = "https://files.pythonhosted.org/packages/03/86/bde4ad8b4d0e9429a4e82c1e8f5c659993a9a863ad62c7df05cf7b678d75/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d9c7f57c3d666a53421049053eaacdd14bbd0a528e2186fcb2e672effd053bb0", size = 150019, upload-time = "2025-10-14T04:40:42.276Z" }, + { url = "https://files.pythonhosted.org/packages/1f/86/a151eb2af293a7e7bac3a739b81072585ce36ccfb4493039f49f1d3cae8c/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:277e970e750505ed74c832b4bf75dac7476262ee2a013f5574dd49075879e161", size = 143310, upload-time = "2025-10-14T04:40:43.439Z" }, + { url = "https://files.pythonhosted.org/packages/b5/fe/43dae6144a7e07b87478fdfc4dbe9efd5defb0e7ec29f5f58a55aeef7bf7/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:31fd66405eaf47bb62e8cd575dc621c56c668f27d46a61d975a249930dd5e2a4", size = 162022, upload-time = "2025-10-14T04:40:44.547Z" }, + { url = "https://files.pythonhosted.org/packages/80/e6/7aab83774f5d2bca81f42ac58d04caf44f0cc2b65fc6db2b3b2e8a05f3b3/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:0d3d8f15c07f86e9ff82319b3d9ef6f4bf907608f53fe9d92b28ea9ae3d1fd89", size = 149383, upload-time = "2025-10-14T04:40:46.018Z" }, + { url = "https://files.pythonhosted.org/packages/4f/e8/b289173b4edae05c0dde07f69f8db476a0b511eac556dfe0d6bda3c43384/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:9f7fcd74d410a36883701fafa2482a6af2ff5ba96b9a620e9e0721e28ead5569", size = 159098, upload-time = "2025-10-14T04:40:47.081Z" }, + { url = "https://files.pythonhosted.org/packages/d8/df/fe699727754cae3f8478493c7f45f777b17c3ef0600e28abfec8619eb49c/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ebf3e58c7ec8a8bed6d66a75d7fb37b55e5015b03ceae72a8e7c74495551e224", size = 152991, upload-time = "2025-10-14T04:40:48.246Z" }, + { url = "https://files.pythonhosted.org/packages/1a/86/584869fe4ddb6ffa3bd9f491b87a01568797fb9bd8933f557dba9771beaf/charset_normalizer-3.4.4-cp311-cp311-win32.whl", hash = "sha256:eecbc200c7fd5ddb9a7f16c7decb07b566c29fa2161a16cf67b8d068bd21690a", size = 99456, upload-time = "2025-10-14T04:40:49.376Z" }, + { url = "https://files.pythonhosted.org/packages/65/f6/62fdd5feb60530f50f7e38b4f6a1d5203f4d16ff4f9f0952962c044e919a/charset_normalizer-3.4.4-cp311-cp311-win_amd64.whl", hash = "sha256:5ae497466c7901d54b639cf42d5b8c1b6a4fead55215500d2f486d34db48d016", size = 106978, upload-time = "2025-10-14T04:40:50.844Z" }, + { url = "https://files.pythonhosted.org/packages/7a/9d/0710916e6c82948b3be62d9d398cb4fcf4e97b56d6a6aeccd66c4b2f2bd5/charset_normalizer-3.4.4-cp311-cp311-win_arm64.whl", hash = "sha256:65e2befcd84bc6f37095f5961e68a6f077bf44946771354a28ad434c2cce0ae1", size = 99969, upload-time = "2025-10-14T04:40:52.272Z" }, + { url = "https://files.pythonhosted.org/packages/f3/85/1637cd4af66fa687396e757dec650f28025f2a2f5a5531a3208dc0ec43f2/charset_normalizer-3.4.4-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0a98e6759f854bd25a58a73fa88833fba3b7c491169f86ce1180c948ab3fd394", size = 208425, upload-time = "2025-10-14T04:40:53.353Z" }, + { url = "https://files.pythonhosted.org/packages/9d/6a/04130023fef2a0d9c62d0bae2649b69f7b7d8d24ea5536feef50551029df/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b5b290ccc2a263e8d185130284f8501e3e36c5e02750fc6b6bdeb2e9e96f1e25", size = 148162, upload-time = "2025-10-14T04:40:54.558Z" }, + { url = "https://files.pythonhosted.org/packages/78/29/62328d79aa60da22c9e0b9a66539feae06ca0f5a4171ac4f7dc285b83688/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:74bb723680f9f7a6234dcf67aea57e708ec1fbdf5699fb91dfd6f511b0a320ef", size = 144558, upload-time = "2025-10-14T04:40:55.677Z" }, + { url = "https://files.pythonhosted.org/packages/86/bb/b32194a4bf15b88403537c2e120b817c61cd4ecffa9b6876e941c3ee38fe/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f1e34719c6ed0b92f418c7c780480b26b5d9c50349e9a9af7d76bf757530350d", size = 161497, upload-time = "2025-10-14T04:40:57.217Z" }, + { url = "https://files.pythonhosted.org/packages/19/89/a54c82b253d5b9b111dc74aca196ba5ccfcca8242d0fb64146d4d3183ff1/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2437418e20515acec67d86e12bf70056a33abdacb5cb1655042f6538d6b085a8", size = 159240, upload-time = "2025-10-14T04:40:58.358Z" }, + { url = "https://files.pythonhosted.org/packages/c0/10/d20b513afe03acc89ec33948320a5544d31f21b05368436d580dec4e234d/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:11d694519d7f29d6cd09f6ac70028dba10f92f6cdd059096db198c283794ac86", size = 153471, upload-time = "2025-10-14T04:40:59.468Z" }, + { url = "https://files.pythonhosted.org/packages/61/fa/fbf177b55bdd727010f9c0a3c49eefa1d10f960e5f09d1d887bf93c2e698/charset_normalizer-3.4.4-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ac1c4a689edcc530fc9d9aa11f5774b9e2f33f9a0c6a57864e90908f5208d30a", size = 150864, upload-time = "2025-10-14T04:41:00.623Z" }, + { url = "https://files.pythonhosted.org/packages/05/12/9fbc6a4d39c0198adeebbde20b619790e9236557ca59fc40e0e3cebe6f40/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:21d142cc6c0ec30d2efee5068ca36c128a30b0f2c53c1c07bd78cb6bc1d3be5f", size = 150647, upload-time = "2025-10-14T04:41:01.754Z" }, + { url = "https://files.pythonhosted.org/packages/ad/1f/6a9a593d52e3e8c5d2b167daf8c6b968808efb57ef4c210acb907c365bc4/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:5dbe56a36425d26d6cfb40ce79c314a2e4dd6211d51d6d2191c00bed34f354cc", size = 145110, upload-time = "2025-10-14T04:41:03.231Z" }, + { url = "https://files.pythonhosted.org/packages/30/42/9a52c609e72471b0fc54386dc63c3781a387bb4fe61c20231a4ebcd58bdd/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:5bfbb1b9acf3334612667b61bd3002196fe2a1eb4dd74d247e0f2a4d50ec9bbf", size = 162839, upload-time = "2025-10-14T04:41:04.715Z" }, + { url = "https://files.pythonhosted.org/packages/c4/5b/c0682bbf9f11597073052628ddd38344a3d673fda35a36773f7d19344b23/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:d055ec1e26e441f6187acf818b73564e6e6282709e9bcb5b63f5b23068356a15", size = 150667, upload-time = "2025-10-14T04:41:05.827Z" }, + { url = "https://files.pythonhosted.org/packages/e4/24/a41afeab6f990cf2daf6cb8c67419b63b48cf518e4f56022230840c9bfb2/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:af2d8c67d8e573d6de5bc30cdb27e9b95e49115cd9baad5ddbd1a6207aaa82a9", size = 160535, upload-time = "2025-10-14T04:41:06.938Z" }, + { url = "https://files.pythonhosted.org/packages/2a/e5/6a4ce77ed243c4a50a1fecca6aaaab419628c818a49434be428fe24c9957/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:780236ac706e66881f3b7f2f32dfe90507a09e67d1d454c762cf642e6e1586e0", size = 154816, upload-time = "2025-10-14T04:41:08.101Z" }, + { url = "https://files.pythonhosted.org/packages/a8/ef/89297262b8092b312d29cdb2517cb1237e51db8ecef2e9af5edbe7b683b1/charset_normalizer-3.4.4-cp312-cp312-win32.whl", hash = "sha256:5833d2c39d8896e4e19b689ffc198f08ea58116bee26dea51e362ecc7cd3ed26", size = 99694, upload-time = "2025-10-14T04:41:09.23Z" }, + { url = "https://files.pythonhosted.org/packages/3d/2d/1e5ed9dd3b3803994c155cd9aacb60c82c331bad84daf75bcb9c91b3295e/charset_normalizer-3.4.4-cp312-cp312-win_amd64.whl", hash = "sha256:a79cfe37875f822425b89a82333404539ae63dbdddf97f84dcbc3d339aae9525", size = 107131, upload-time = "2025-10-14T04:41:10.467Z" }, + { url = "https://files.pythonhosted.org/packages/d0/d9/0ed4c7098a861482a7b6a95603edce4c0d9db2311af23da1fb2b75ec26fc/charset_normalizer-3.4.4-cp312-cp312-win_arm64.whl", hash = "sha256:376bec83a63b8021bb5c8ea75e21c4ccb86e7e45ca4eb81146091b56599b80c3", size = 100390, upload-time = "2025-10-14T04:41:11.915Z" }, + { url = "https://files.pythonhosted.org/packages/97/45/4b3a1239bbacd321068ea6e7ac28875b03ab8bc0aa0966452db17cd36714/charset_normalizer-3.4.4-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:e1f185f86a6f3403aa2420e815904c67b2f9ebc443f045edd0de921108345794", size = 208091, upload-time = "2025-10-14T04:41:13.346Z" }, + { url = "https://files.pythonhosted.org/packages/7d/62/73a6d7450829655a35bb88a88fca7d736f9882a27eacdca2c6d505b57e2e/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b39f987ae8ccdf0d2642338faf2abb1862340facc796048b604ef14919e55ed", size = 147936, upload-time = "2025-10-14T04:41:14.461Z" }, + { url = "https://files.pythonhosted.org/packages/89/c5/adb8c8b3d6625bef6d88b251bbb0d95f8205831b987631ab0c8bb5d937c2/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3162d5d8ce1bb98dd51af660f2121c55d0fa541b46dff7bb9b9f86ea1d87de72", size = 144180, upload-time = "2025-10-14T04:41:15.588Z" }, + { url = "https://files.pythonhosted.org/packages/91/ed/9706e4070682d1cc219050b6048bfd293ccf67b3d4f5a4f39207453d4b99/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:81d5eb2a312700f4ecaa977a8235b634ce853200e828fbadf3a9c50bab278328", size = 161346, upload-time = "2025-10-14T04:41:16.738Z" }, + { url = "https://files.pythonhosted.org/packages/d5/0d/031f0d95e4972901a2f6f09ef055751805ff541511dc1252ba3ca1f80cf5/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5bd2293095d766545ec1a8f612559f6b40abc0eb18bb2f5d1171872d34036ede", size = 158874, upload-time = "2025-10-14T04:41:17.923Z" }, + { url = "https://files.pythonhosted.org/packages/f5/83/6ab5883f57c9c801ce5e5677242328aa45592be8a00644310a008d04f922/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a8a8b89589086a25749f471e6a900d3f662d1d3b6e2e59dcecf787b1cc3a1894", size = 153076, upload-time = "2025-10-14T04:41:19.106Z" }, + { url = "https://files.pythonhosted.org/packages/75/1e/5ff781ddf5260e387d6419959ee89ef13878229732732ee73cdae01800f2/charset_normalizer-3.4.4-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc7637e2f80d8530ee4a78e878bce464f70087ce73cf7c1caf142416923b98f1", size = 150601, upload-time = "2025-10-14T04:41:20.245Z" }, + { url = "https://files.pythonhosted.org/packages/d7/57/71be810965493d3510a6ca79b90c19e48696fb1ff964da319334b12677f0/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f8bf04158c6b607d747e93949aa60618b61312fe647a6369f88ce2ff16043490", size = 150376, upload-time = "2025-10-14T04:41:21.398Z" }, + { url = "https://files.pythonhosted.org/packages/e5/d5/c3d057a78c181d007014feb7e9f2e65905a6c4ef182c0ddf0de2924edd65/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:554af85e960429cf30784dd47447d5125aaa3b99a6f0683589dbd27e2f45da44", size = 144825, upload-time = "2025-10-14T04:41:22.583Z" }, + { url = "https://files.pythonhosted.org/packages/e6/8c/d0406294828d4976f275ffbe66f00266c4b3136b7506941d87c00cab5272/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:74018750915ee7ad843a774364e13a3db91682f26142baddf775342c3f5b1133", size = 162583, upload-time = "2025-10-14T04:41:23.754Z" }, + { url = "https://files.pythonhosted.org/packages/d7/24/e2aa1f18c8f15c4c0e932d9287b8609dd30ad56dbe41d926bd846e22fb8d/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:c0463276121fdee9c49b98908b3a89c39be45d86d1dbaa22957e38f6321d4ce3", size = 150366, upload-time = "2025-10-14T04:41:25.27Z" }, + { url = "https://files.pythonhosted.org/packages/e4/5b/1e6160c7739aad1e2df054300cc618b06bf784a7a164b0f238360721ab86/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:362d61fd13843997c1c446760ef36f240cf81d3ebf74ac62652aebaf7838561e", size = 160300, upload-time = "2025-10-14T04:41:26.725Z" }, + { url = "https://files.pythonhosted.org/packages/7a/10/f882167cd207fbdd743e55534d5d9620e095089d176d55cb22d5322f2afd/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9a26f18905b8dd5d685d6d07b0cdf98a79f3c7a918906af7cc143ea2e164c8bc", size = 154465, upload-time = "2025-10-14T04:41:28.322Z" }, + { url = "https://files.pythonhosted.org/packages/89/66/c7a9e1b7429be72123441bfdbaf2bc13faab3f90b933f664db506dea5915/charset_normalizer-3.4.4-cp313-cp313-win32.whl", hash = "sha256:9b35f4c90079ff2e2edc5b26c0c77925e5d2d255c42c74fdb70fb49b172726ac", size = 99404, upload-time = "2025-10-14T04:41:29.95Z" }, + { url = "https://files.pythonhosted.org/packages/c4/26/b9924fa27db384bdcd97ab83b4f0a8058d96ad9626ead570674d5e737d90/charset_normalizer-3.4.4-cp313-cp313-win_amd64.whl", hash = "sha256:b435cba5f4f750aa6c0a0d92c541fb79f69a387c91e61f1795227e4ed9cece14", size = 107092, upload-time = "2025-10-14T04:41:31.188Z" }, + { url = "https://files.pythonhosted.org/packages/af/8f/3ed4bfa0c0c72a7ca17f0380cd9e4dd842b09f664e780c13cff1dcf2ef1b/charset_normalizer-3.4.4-cp313-cp313-win_arm64.whl", hash = "sha256:542d2cee80be6f80247095cc36c418f7bddd14f4a6de45af91dfad36d817bba2", size = 100408, upload-time = "2025-10-14T04:41:32.624Z" }, + { url = "https://files.pythonhosted.org/packages/2a/35/7051599bd493e62411d6ede36fd5af83a38f37c4767b92884df7301db25d/charset_normalizer-3.4.4-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:da3326d9e65ef63a817ecbcc0df6e94463713b754fe293eaa03da99befb9a5bd", size = 207746, upload-time = "2025-10-14T04:41:33.773Z" }, + { url = "https://files.pythonhosted.org/packages/10/9a/97c8d48ef10d6cd4fcead2415523221624bf58bcf68a802721a6bc807c8f/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8af65f14dc14a79b924524b1e7fffe304517b2bff5a58bf64f30b98bbc5079eb", size = 147889, upload-time = "2025-10-14T04:41:34.897Z" }, + { url = "https://files.pythonhosted.org/packages/10/bf/979224a919a1b606c82bd2c5fa49b5c6d5727aa47b4312bb27b1734f53cd/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:74664978bb272435107de04e36db5a9735e78232b85b77d45cfb38f758efd33e", size = 143641, upload-time = "2025-10-14T04:41:36.116Z" }, + { url = "https://files.pythonhosted.org/packages/ba/33/0ad65587441fc730dc7bd90e9716b30b4702dc7b617e6ba4997dc8651495/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:752944c7ffbfdd10c074dc58ec2d5a8a4cd9493b314d367c14d24c17684ddd14", size = 160779, upload-time = "2025-10-14T04:41:37.229Z" }, + { url = "https://files.pythonhosted.org/packages/67/ed/331d6b249259ee71ddea93f6f2f0a56cfebd46938bde6fcc6f7b9a3d0e09/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d1f13550535ad8cff21b8d757a3257963e951d96e20ec82ab44bc64aeb62a191", size = 159035, upload-time = "2025-10-14T04:41:38.368Z" }, + { url = "https://files.pythonhosted.org/packages/67/ff/f6b948ca32e4f2a4576aa129d8bed61f2e0543bf9f5f2b7fc3758ed005c9/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ecaae4149d99b1c9e7b88bb03e3221956f68fd6d50be2ef061b2381b61d20838", size = 152542, upload-time = "2025-10-14T04:41:39.862Z" }, + { url = "https://files.pythonhosted.org/packages/16/85/276033dcbcc369eb176594de22728541a925b2632f9716428c851b149e83/charset_normalizer-3.4.4-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:cb6254dc36b47a990e59e1068afacdcd02958bdcce30bb50cc1700a8b9d624a6", size = 149524, upload-time = "2025-10-14T04:41:41.319Z" }, + { url = "https://files.pythonhosted.org/packages/9e/f2/6a2a1f722b6aba37050e626530a46a68f74e63683947a8acff92569f979a/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c8ae8a0f02f57a6e61203a31428fa1d677cbe50c93622b4149d5c0f319c1d19e", size = 150395, upload-time = "2025-10-14T04:41:42.539Z" }, + { url = "https://files.pythonhosted.org/packages/60/bb/2186cb2f2bbaea6338cad15ce23a67f9b0672929744381e28b0592676824/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:47cc91b2f4dd2833fddaedd2893006b0106129d4b94fdb6af1f4ce5a9965577c", size = 143680, upload-time = "2025-10-14T04:41:43.661Z" }, + { url = "https://files.pythonhosted.org/packages/7d/a5/bf6f13b772fbb2a90360eb620d52ed8f796f3c5caee8398c3b2eb7b1c60d/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:82004af6c302b5d3ab2cfc4cc5f29db16123b1a8417f2e25f9066f91d4411090", size = 162045, upload-time = "2025-10-14T04:41:44.821Z" }, + { url = "https://files.pythonhosted.org/packages/df/c5/d1be898bf0dc3ef9030c3825e5d3b83f2c528d207d246cbabe245966808d/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:2b7d8f6c26245217bd2ad053761201e9f9680f8ce52f0fcd8d0755aeae5b2152", size = 149687, upload-time = "2025-10-14T04:41:46.442Z" }, + { url = "https://files.pythonhosted.org/packages/a5/42/90c1f7b9341eef50c8a1cb3f098ac43b0508413f33affd762855f67a410e/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:799a7a5e4fb2d5898c60b640fd4981d6a25f1c11790935a44ce38c54e985f828", size = 160014, upload-time = "2025-10-14T04:41:47.631Z" }, + { url = "https://files.pythonhosted.org/packages/76/be/4d3ee471e8145d12795ab655ece37baed0929462a86e72372fd25859047c/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:99ae2cffebb06e6c22bdc25801d7b30f503cc87dbd283479e7b606f70aff57ec", size = 154044, upload-time = "2025-10-14T04:41:48.81Z" }, + { url = "https://files.pythonhosted.org/packages/b0/6f/8f7af07237c34a1defe7defc565a9bc1807762f672c0fde711a4b22bf9c0/charset_normalizer-3.4.4-cp314-cp314-win32.whl", hash = "sha256:f9d332f8c2a2fcbffe1378594431458ddbef721c1769d78e2cbc06280d8155f9", size = 99940, upload-time = "2025-10-14T04:41:49.946Z" }, + { url = "https://files.pythonhosted.org/packages/4b/51/8ade005e5ca5b0d80fb4aff72a3775b325bdc3d27408c8113811a7cbe640/charset_normalizer-3.4.4-cp314-cp314-win_amd64.whl", hash = "sha256:8a6562c3700cce886c5be75ade4a5db4214fda19fede41d9792d100288d8f94c", size = 107104, upload-time = "2025-10-14T04:41:51.051Z" }, + { url = "https://files.pythonhosted.org/packages/da/5f/6b8f83a55bb8278772c5ae54a577f3099025f9ade59d0136ac24a0df4bde/charset_normalizer-3.4.4-cp314-cp314-win_arm64.whl", hash = "sha256:de00632ca48df9daf77a2c65a484531649261ec9f25489917f09e455cb09ddb2", size = 100743, upload-time = "2025-10-14T04:41:52.122Z" }, + { url = "https://files.pythonhosted.org/packages/0a/4c/925909008ed5a988ccbb72dcc897407e5d6d3bd72410d69e051fc0c14647/charset_normalizer-3.4.4-py3-none-any.whl", hash = "sha256:7a32c560861a02ff789ad905a2fe94e3f840803362c84fecf1851cb4cf3dc37f", size = 53402, upload-time = "2025-10-14T04:42:31.76Z" }, +] + +[[package]] +name = "google-api-core" +version = "2.28.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "google-auth" }, + { name = "googleapis-common-protos" }, + { name = "proto-plus" }, + { name = "protobuf" }, + { name = "requests" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/61/da/83d7043169ac2c8c7469f0e375610d78ae2160134bf1b80634c482fa079c/google_api_core-2.28.1.tar.gz", hash = "sha256:2b405df02d68e68ce0fbc138559e6036559e685159d148ae5861013dc201baf8", size = 176759, upload-time = "2025-10-28T21:34:51.529Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ed/d4/90197b416cb61cefd316964fd9e7bd8324bcbafabf40eef14a9f20b81974/google_api_core-2.28.1-py3-none-any.whl", hash = "sha256:4021b0f8ceb77a6fb4de6fde4502cecab45062e66ff4f2895169e0b35bc9466c", size = 173706, upload-time = "2025-10-28T21:34:50.151Z" }, +] + +[[package]] +name = "google-api-python-client" +version = "2.187.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "google-api-core" }, + { name = "google-auth" }, + { name = "google-auth-httplib2" }, + { name = "httplib2" }, + { name = "uritemplate" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/75/83/60cdacf139d768dd7f0fcbe8d95b418299810068093fdf8228c6af89bb70/google_api_python_client-2.187.0.tar.gz", hash = "sha256:e98e8e8f49e1b5048c2f8276473d6485febc76c9c47892a8b4d1afa2c9ec8278", size = 14068154, upload-time = "2025-11-06T01:48:53.274Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/96/58/c1e716be1b055b504d80db2c8413f6c6a890a6ae218a65f178b63bc30356/google_api_python_client-2.187.0-py3-none-any.whl", hash = "sha256:d8d0f6d85d7d1d10bdab32e642312ed572bdc98919f72f831b44b9a9cebba32f", size = 14641434, upload-time = "2025-11-06T01:48:50.763Z" }, +] + +[[package]] +name = "google-auth" +version = "2.41.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cachetools" }, + { name = "pyasn1-modules" }, + { name = "rsa" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a8/af/5129ce5b2f9688d2fa49b463e544972a7c82b0fdb50980dafee92e121d9f/google_auth-2.41.1.tar.gz", hash = "sha256:b76b7b1f9e61f0cb7e88870d14f6a94aeef248959ef6992670efee37709cbfd2", size = 292284, upload-time = "2025-09-30T22:51:26.363Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/be/a4/7319a2a8add4cc352be9e3efeff5e2aacee917c85ca2fa1647e29089983c/google_auth-2.41.1-py2.py3-none-any.whl", hash = "sha256:754843be95575b9a19c604a848a41be03f7f2afd8c019f716dc1f51ee41c639d", size = 221302, upload-time = "2025-09-30T22:51:24.212Z" }, +] + +[[package]] +name = "google-auth-httplib2" +version = "0.3.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "google-auth" }, + { name = "httplib2" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d5/ad/c1f2b1175096a8d04cf202ad5ea6065f108d26be6fc7215876bde4a7981d/google_auth_httplib2-0.3.0.tar.gz", hash = "sha256:177898a0175252480d5ed916aeea183c2df87c1f9c26705d74ae6b951c268b0b", size = 11134, upload-time = "2025-12-15T22:13:51.825Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/99/d5/3c97526c8796d3caf5f4b3bed2b05e8a7102326f00a334e7a438237f3b22/google_auth_httplib2-0.3.0-py3-none-any.whl", hash = "sha256:426167e5df066e3f5a0fc7ea18768c08e7296046594ce4c8c409c2457dd1f776", size = 9529, upload-time = "2025-12-15T22:13:51.048Z" }, +] + +[[package]] +name = "google-auth-oauthlib" +version = "1.2.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "google-auth" }, + { name = "requests-oauthlib" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/86/a6/c6336a6ceb682709a4aa39e2e6b5754a458075ca92359512b6cbfcb25ae3/google_auth_oauthlib-1.2.3.tar.gz", hash = "sha256:eb09e450d3cc789ecbc2b3529cb94a713673fd5f7a22c718ad91cf75aedc2ea4", size = 21265, upload-time = "2025-10-30T21:28:19.105Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/38/07/a54c100da461ffc5968457823fcc665a48fb4b875c68bcfecbfe24a10dbe/google_auth_oauthlib-1.2.3-py3-none-any.whl", hash = "sha256:7c0940e037677f25e71999607493640d071212e7f3c15aa0febea4c47a5a0680", size = 19184, upload-time = "2025-10-30T21:28:17.88Z" }, +] + +[[package]] +name = "googleapis-common-protos" +version = "1.72.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "protobuf" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e5/7b/adfd75544c415c487b33061fe7ae526165241c1ea133f9a9125a56b39fd8/googleapis_common_protos-1.72.0.tar.gz", hash = "sha256:e55a601c1b32b52d7a3e65f43563e2aa61bcd737998ee672ac9b951cd49319f5", size = 147433, upload-time = "2025-11-06T18:29:24.087Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c4/ab/09169d5a4612a5f92490806649ac8d41e3ec9129c636754575b3553f4ea4/googleapis_common_protos-1.72.0-py3-none-any.whl", hash = "sha256:4299c5a82d5ae1a9702ada957347726b167f9f8d1fc352477702a1e851ff4038", size = 297515, upload-time = "2025-11-06T18:29:13.14Z" }, +] + +[[package]] +name = "httplib2" +version = "0.31.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pyparsing" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/52/77/6653db69c1f7ecfe5e3f9726fdadc981794656fcd7d98c4209fecfea9993/httplib2-0.31.0.tar.gz", hash = "sha256:ac7ab497c50975147d4f7b1ade44becc7df2f8954d42b38b3d69c515f531135c", size = 250759, upload-time = "2025-09-11T12:16:03.403Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8c/a2/0d269db0f6163be503775dc8b6a6fa15820cc9fdc866f6ba608d86b721f2/httplib2-0.31.0-py3-none-any.whl", hash = "sha256:b9cd78abea9b4e43a7714c6e0f8b6b8561a6fc1e95d5dbd367f5bf0ef35f5d24", size = 91148, upload-time = "2025-09-11T12:16:01.803Z" }, +] + +[[package]] +name = "idna" +version = "3.11" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/0703ccc57f3a7233505399edb88de3cbd678da106337b9fcde432b65ed60/idna-3.11.tar.gz", hash = "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902", size = 194582, upload-time = "2025-10-12T14:55:20.501Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea", size = 71008, upload-time = "2025-10-12T14:55:18.883Z" }, +] + +[[package]] +name = "oauthlib" +version = "3.3.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/0b/5f/19930f824ffeb0ad4372da4812c50edbd1434f678c90c2733e1188edfc63/oauthlib-3.3.1.tar.gz", hash = "sha256:0f0f8aa759826a193cf66c12ea1af1637f87b9b4622d46e866952bb022e538c9", size = 185918, upload-time = "2025-06-19T22:48:08.269Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/be/9c/92789c596b8df838baa98fa71844d84283302f7604ed565dafe5a6b5041a/oauthlib-3.3.1-py3-none-any.whl", hash = "sha256:88119c938d2b8fb88561af5f6ee0eec8cc8d552b7bb1f712743136eb7523b7a1", size = 160065, upload-time = "2025-06-19T22:48:06.508Z" }, +] + +[[package]] +name = "portfolio" +version = "0.1.0" +source = { virtual = "." } +dependencies = [ + { name = "google-api-python-client" }, + { name = "google-auth-oauthlib" }, +] + +[package.metadata] +requires-dist = [ + { name = "google-api-python-client", specifier = ">=2.187.0" }, + { name = "google-auth-oauthlib", specifier = ">=1.2.3" }, +] + +[[package]] +name = "proto-plus" +version = "1.27.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "protobuf" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/01/89/9cbe2f4bba860e149108b683bc2efec21f14d5f7ed6e25562ad86acbc373/proto_plus-1.27.0.tar.gz", hash = "sha256:873af56dd0d7e91836aee871e5799e1c6f1bda86ac9a983e0bb9f0c266a568c4", size = 56158, upload-time = "2025-12-16T13:46:25.729Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cd/24/3b7a0818484df9c28172857af32c2397b6d8fcd99d9468bd4684f98ebf0a/proto_plus-1.27.0-py3-none-any.whl", hash = "sha256:1baa7f81cf0f8acb8bc1f6d085008ba4171eaf669629d1b6d1673b21ed1c0a82", size = 50205, upload-time = "2025-12-16T13:46:24.76Z" }, +] + +[[package]] +name = "protobuf" +version = "6.33.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/34/44/e49ecff446afeec9d1a66d6bbf9adc21e3c7cea7803a920ca3773379d4f6/protobuf-6.33.2.tar.gz", hash = "sha256:56dc370c91fbb8ac85bc13582c9e373569668a290aa2e66a590c2a0d35ddb9e4", size = 444296, upload-time = "2025-12-06T00:17:53.311Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bc/91/1e3a34881a88697a7354ffd177e8746e97a722e5e8db101544b47e84afb1/protobuf-6.33.2-cp310-abi3-win32.whl", hash = "sha256:87eb388bd2d0f78febd8f4c8779c79247b26a5befad525008e49a6955787ff3d", size = 425603, upload-time = "2025-12-06T00:17:41.114Z" }, + { url = "https://files.pythonhosted.org/packages/64/20/4d50191997e917ae13ad0a235c8b42d8c1ab9c3e6fd455ca16d416944355/protobuf-6.33.2-cp310-abi3-win_amd64.whl", hash = "sha256:fc2a0e8b05b180e5fc0dd1559fe8ebdae21a27e81ac77728fb6c42b12c7419b4", size = 436930, upload-time = "2025-12-06T00:17:43.278Z" }, + { url = "https://files.pythonhosted.org/packages/b2/ca/7e485da88ba45c920fb3f50ae78de29ab925d9e54ef0de678306abfbb497/protobuf-6.33.2-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:d9b19771ca75935b3a4422957bc518b0cecb978b31d1dd12037b088f6bcc0e43", size = 427621, upload-time = "2025-12-06T00:17:44.445Z" }, + { url = "https://files.pythonhosted.org/packages/7d/4f/f743761e41d3b2b2566748eb76bbff2b43e14d5fcab694f494a16458b05f/protobuf-6.33.2-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:b5d3b5625192214066d99b2b605f5783483575656784de223f00a8d00754fc0e", size = 324460, upload-time = "2025-12-06T00:17:45.678Z" }, + { url = "https://files.pythonhosted.org/packages/b1/fa/26468d00a92824020f6f2090d827078c09c9c587e34cbfd2d0c7911221f8/protobuf-6.33.2-cp39-abi3-manylinux2014_s390x.whl", hash = "sha256:8cd7640aee0b7828b6d03ae518b5b4806fdfc1afe8de82f79c3454f8aef29872", size = 339168, upload-time = "2025-12-06T00:17:46.813Z" }, + { url = "https://files.pythonhosted.org/packages/56/13/333b8f421738f149d4fe5e49553bc2a2ab75235486259f689b4b91f96cec/protobuf-6.33.2-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:1f8017c48c07ec5859106533b682260ba3d7c5567b1ca1f24297ce03384d1b4f", size = 323270, upload-time = "2025-12-06T00:17:48.253Z" }, + { url = "https://files.pythonhosted.org/packages/0e/15/4f02896cc3df04fc465010a4c6a0cd89810f54617a32a70ef531ed75d61c/protobuf-6.33.2-py3-none-any.whl", hash = "sha256:7636aad9bb01768870266de5dc009de2d1b936771b38a793f73cbbf279c91c5c", size = 170501, upload-time = "2025-12-06T00:17:52.211Z" }, +] + +[[package]] +name = "pyasn1" +version = "0.6.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ba/e9/01f1a64245b89f039897cb0130016d79f77d52669aae6ee7b159a6c4c018/pyasn1-0.6.1.tar.gz", hash = "sha256:6f580d2bdd84365380830acf45550f2511469f673cb4a5ae3857a3170128b034", size = 145322, upload-time = "2024-09-10T22:41:42.55Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c8/f1/d6a797abb14f6283c0ddff96bbdd46937f64122b8c925cab503dd37f8214/pyasn1-0.6.1-py3-none-any.whl", hash = "sha256:0d632f46f2ba09143da3a8afe9e33fb6f92fa2320ab7e886e2d0f7672af84629", size = 83135, upload-time = "2024-09-11T16:00:36.122Z" }, +] + +[[package]] +name = "pyasn1-modules" +version = "0.4.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pyasn1" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e9/e6/78ebbb10a8c8e4b61a59249394a4a594c1a7af95593dc933a349c8d00964/pyasn1_modules-0.4.2.tar.gz", hash = "sha256:677091de870a80aae844b1ca6134f54652fa2c8c5a52aa396440ac3106e941e6", size = 307892, upload-time = "2025-03-28T02:41:22.17Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/47/8d/d529b5d697919ba8c11ad626e835d4039be708a35b0d22de83a269a6682c/pyasn1_modules-0.4.2-py3-none-any.whl", hash = "sha256:29253a9207ce32b64c3ac6600edc75368f98473906e8fd1043bd6b5b1de2c14a", size = 181259, upload-time = "2025-03-28T02:41:19.028Z" }, +] + +[[package]] +name = "pyparsing" +version = "3.2.5" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f2/a5/181488fc2b9d093e3972d2a472855aae8a03f000592dbfce716a512b3359/pyparsing-3.2.5.tar.gz", hash = "sha256:2df8d5b7b2802ef88e8d016a2eb9c7aeaa923529cd251ed0fe4608275d4105b6", size = 1099274, upload-time = "2025-09-21T04:11:06.277Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/10/5e/1aa9a93198c6b64513c9d7752de7422c06402de6600a8767da1524f9570b/pyparsing-3.2.5-py3-none-any.whl", hash = "sha256:e38a4f02064cf41fe6593d328d0512495ad1f3d8a91c4f73fc401b3079a59a5e", size = 113890, upload-time = "2025-09-21T04:11:04.117Z" }, +] + +[[package]] +name = "requests" +version = "2.32.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "charset-normalizer" }, + { name = "idna" }, + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c9/74/b3ff8e6c8446842c3f5c837e9c3dfcfe2018ea6ecef224c710c85ef728f4/requests-2.32.5.tar.gz", hash = "sha256:dbba0bac56e100853db0ea71b82b4dfd5fe2bf6d3754a8893c3af500cec7d7cf", size = 134517, upload-time = "2025-08-18T20:46:02.573Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1e/db/4254e3eabe8020b458f1a747140d32277ec7a271daf1d235b70dc0b4e6e3/requests-2.32.5-py3-none-any.whl", hash = "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6", size = 64738, upload-time = "2025-08-18T20:46:00.542Z" }, +] + +[[package]] +name = "requests-oauthlib" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "oauthlib" }, + { name = "requests" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/42/f2/05f29bc3913aea15eb670be136045bf5c5bbf4b99ecb839da9b422bb2c85/requests-oauthlib-2.0.0.tar.gz", hash = "sha256:b3dffaebd884d8cd778494369603a9e7b58d29111bf6b41bdc2dcd87203af4e9", size = 55650, upload-time = "2024-03-22T20:32:29.939Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3b/5d/63d4ae3b9daea098d5d6f5da83984853c1bbacd5dc826764b249fe119d24/requests_oauthlib-2.0.0-py2.py3-none-any.whl", hash = "sha256:7dd8a5c40426b779b0868c404bdef9768deccf22749cde15852df527e6269b36", size = 24179, upload-time = "2024-03-22T20:32:28.055Z" }, +] + +[[package]] +name = "rsa" +version = "4.9.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pyasn1" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/da/8a/22b7beea3ee0d44b1916c0c1cb0ee3af23b700b6da9f04991899d0c555d4/rsa-4.9.1.tar.gz", hash = "sha256:e7bdbfdb5497da4c07dfd35530e1a902659db6ff241e39d9953cad06ebd0ae75", size = 29034, upload-time = "2025-04-16T09:51:18.218Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/64/8d/0133e4eb4beed9e425d9a98ed6e081a55d195481b7632472be1af08d2f6b/rsa-4.9.1-py3-none-any.whl", hash = "sha256:68635866661c6836b8d39430f97a996acbd61bfa49406748ea243539fe239762", size = 34696, upload-time = "2025-04-16T09:51:17.142Z" }, +] + +[[package]] +name = "uritemplate" +version = "4.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/98/60/f174043244c5306c9988380d2cb10009f91563fc4b31293d27e17201af56/uritemplate-4.2.0.tar.gz", hash = "sha256:480c2ed180878955863323eea31b0ede668795de182617fef9c6ca09e6ec9d0e", size = 33267, upload-time = "2025-06-02T15:12:06.318Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a9/99/3ae339466c9183ea5b8ae87b34c0b897eda475d2aec2307cae60e5cd4f29/uritemplate-4.2.0-py3-none-any.whl", hash = "sha256:962201ba1c4edcab02e60f9a0d3821e82dfc5d2d6662a21abd533879bdb8a686", size = 11488, upload-time = "2025-06-02T15:12:03.405Z" }, +] + +[[package]] +name = "urllib3" +version = "2.6.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1e/24/a2a2ed9addd907787d7aa0355ba36a6cadf1768b934c652ea78acbd59dcd/urllib3-2.6.2.tar.gz", hash = "sha256:016f9c98bb7e98085cb2b4b17b87d2c702975664e4f060c6532e64d1c1a5e797", size = 432930, upload-time = "2025-12-11T15:56:40.252Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6d/b9/4095b668ea3678bf6a0af005527f39de12fb026516fb3df17495a733b7f8/urllib3-2.6.2-py3-none-any.whl", hash = "sha256:ec21cddfe7724fc7cb4ba4bea7aa8e2ef36f607a4bab81aa6ce42a13dc3f03dd", size = 131182, upload-time = "2025-12-11T15:56:38.584Z" }, +]