sparq 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- sparq-0.1.0/LICENSE +21 -0
- sparq-0.1.0/PKG-INFO +23 -0
- sparq-0.1.0/README.md +8 -0
- sparq-0.1.0/auth.py +60 -0
- sparq-0.1.0/client.py +63 -0
- sparq-0.1.0/pyproject.toml +27 -0
- sparq-0.1.0/recover.py +54 -0
- sparq-0.1.0/setup.cfg +4 -0
- sparq-0.1.0/sparq.egg-info/PKG-INFO +23 -0
- sparq-0.1.0/sparq.egg-info/SOURCES.txt +13 -0
- sparq-0.1.0/sparq.egg-info/dependency_links.txt +1 -0
- sparq-0.1.0/sparq.egg-info/requires.txt +1 -0
- sparq-0.1.0/sparq.egg-info/top_level.txt +5 -0
- sparq-0.1.0/sparq.py +39 -0
- sparq-0.1.0/usage.py +55 -0
sparq-0.1.0/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2025 Shiven Sheth
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
sparq-0.1.0/PKG-INFO
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
Metadata-Version: 2.4
|
2
|
+
Name: sparq
|
3
|
+
Version: 0.1.0
|
4
|
+
Summary: Python client for the sparq api - automated degree planning for SJSU students + more
|
5
|
+
Author: Shiven Sheth
|
6
|
+
License: MIT
|
7
|
+
Project-URL: Homepage, https://github.com/shiventi/sparq
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
9
|
+
Classifier: Operating System :: OS Independent
|
10
|
+
Requires-Python: >=3.8
|
11
|
+
Description-Content-Type: text/markdown
|
12
|
+
License-File: LICENSE
|
13
|
+
Requires-Dist: requests>=2.25.0
|
14
|
+
Dynamic: license-file
|
15
|
+
|
16
|
+
# sparq Client
|
17
|
+
|
18
|
+
Python client library for the sparq API - automated degree planning for SJSU students.
|
19
|
+
|
20
|
+
## Installation
|
21
|
+
|
22
|
+
```bash
|
23
|
+
pip install sparq
|
sparq-0.1.0/README.md
ADDED
sparq-0.1.0/auth.py
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
|
3
|
+
import requests
|
4
|
+
import os
|
5
|
+
from pathlib import Path
|
6
|
+
|
7
|
+
API_URL = "https://sparq-api.onrender.com"
|
8
|
+
|
9
|
+
def save_api_key(api_key, email):
|
10
|
+
config_dir = Path.home() / ".sparq"
|
11
|
+
config_dir.mkdir(exist_ok=True)
|
12
|
+
config_file = config_dir / "config.txt"
|
13
|
+
|
14
|
+
with open(config_file, "w") as f:
|
15
|
+
f.write(f"API_KEY={api_key}\n")
|
16
|
+
f.write(f"EMAIL={email}\n")
|
17
|
+
|
18
|
+
print(f"\nAPI key saved to {config_file}")
|
19
|
+
|
20
|
+
def register():
|
21
|
+
email = input("Email: ").strip()
|
22
|
+
|
23
|
+
if not email or "@" not in email:
|
24
|
+
print("Invalid email address")
|
25
|
+
return
|
26
|
+
|
27
|
+
response = requests.post(
|
28
|
+
f"{API_URL}/register",
|
29
|
+
json={"email": email},
|
30
|
+
headers={"Content-Type": "application/json"}
|
31
|
+
)
|
32
|
+
|
33
|
+
if response.status_code == 200:
|
34
|
+
data = response.json()
|
35
|
+
if data.get("status") == "pending_verification":
|
36
|
+
print("Code already sent. Check your email.")
|
37
|
+
else:
|
38
|
+
print("Verification code sent to your email")
|
39
|
+
elif response.status_code != 200:
|
40
|
+
print(response.json()['detail'])
|
41
|
+
return
|
42
|
+
|
43
|
+
code = input("Enter code: ").strip()
|
44
|
+
|
45
|
+
response = requests.post(
|
46
|
+
f"{API_URL}/verify",
|
47
|
+
json={"email": email, "code": code},
|
48
|
+
headers={"Content-Type": "application/json"}
|
49
|
+
)
|
50
|
+
|
51
|
+
if response.status_code == 200:
|
52
|
+
api_key = response.json()["api_key"]
|
53
|
+
print(f"\nAPI Key: {api_key}")
|
54
|
+
save_api_key(api_key, email)
|
55
|
+
else:
|
56
|
+
print(response.json()['detail'])
|
57
|
+
|
58
|
+
if __name__ == "__main__":
|
59
|
+
register()
|
60
|
+
|
sparq-0.1.0/client.py
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
|
3
|
+
import requests
|
4
|
+
from pathlib import Path
|
5
|
+
|
6
|
+
API_URL = "https://sparq-api.onrender.com"
|
7
|
+
|
8
|
+
class Sparq:
|
9
|
+
def __init__(self, api_key=None):
|
10
|
+
if api_key:
|
11
|
+
self.api_key = api_key
|
12
|
+
else:
|
13
|
+
self.api_key = self._load_api_key()
|
14
|
+
|
15
|
+
def _load_api_key(self):
|
16
|
+
config_file = Path.home() / ".sparq" / "config.txt"
|
17
|
+
if config_file.exists():
|
18
|
+
with open(config_file, "r") as f:
|
19
|
+
for line in f:
|
20
|
+
if line.startswith("API_KEY="):
|
21
|
+
return line.strip().split("=", 1)[1]
|
22
|
+
return None
|
23
|
+
|
24
|
+
def plan(self, major, cc_courses=None, ap_exams=None, sjsu_courses=None, units_per_semester=15):
|
25
|
+
if not self.api_key:
|
26
|
+
raise Exception("No API key found. Run auth.py first.")
|
27
|
+
|
28
|
+
payload = {
|
29
|
+
"major": major,
|
30
|
+
"cc_courses": cc_courses or [],
|
31
|
+
"ap_exams": ap_exams or [],
|
32
|
+
"sjsu_courses": sjsu_courses or [],
|
33
|
+
"units_per_semester": units_per_semester
|
34
|
+
}
|
35
|
+
|
36
|
+
response = requests.post(
|
37
|
+
f"{API_URL}/plan",
|
38
|
+
json=payload,
|
39
|
+
headers={
|
40
|
+
"Content-Type": "application/json",
|
41
|
+
"x-api-key": self.api_key
|
42
|
+
}
|
43
|
+
)
|
44
|
+
|
45
|
+
if response.status_code == 200:
|
46
|
+
return response.json()
|
47
|
+
else:
|
48
|
+
raise Exception(f"Error: {response.json().get('detail', 'Unknown error')}")
|
49
|
+
|
50
|
+
def usage(self):
|
51
|
+
"""Get API usage statistics for the authenticated user."""
|
52
|
+
if not self.api_key:
|
53
|
+
raise Exception("No API key found. Run auth.py first.")
|
54
|
+
|
55
|
+
response = requests.get(
|
56
|
+
f"{API_URL}/usage",
|
57
|
+
params={"x_api_key": self.api_key}
|
58
|
+
)
|
59
|
+
|
60
|
+
if response.status_code == 200:
|
61
|
+
return response.json()
|
62
|
+
else:
|
63
|
+
raise Exception(f"Error: {response.json().get('detail', 'Unknown error')}")
|
@@ -0,0 +1,27 @@
|
|
1
|
+
[build-system]
|
2
|
+
requires = ["setuptools>=61.0", "wheel"]
|
3
|
+
build-backend = "setuptools.build_meta"
|
4
|
+
|
5
|
+
[project]
|
6
|
+
name = "sparq"
|
7
|
+
version = "0.1.0"
|
8
|
+
authors = [
|
9
|
+
{ name="Shiven Sheth" }
|
10
|
+
]
|
11
|
+
description = "Python client for the sparq api - automated degree planning for SJSU students + more"
|
12
|
+
readme = "README.md"
|
13
|
+
requires-python = ">=3.8"
|
14
|
+
license = {text = "MIT"}
|
15
|
+
classifiers = [
|
16
|
+
"Programming Language :: Python :: 3",
|
17
|
+
"Operating System :: OS Independent",
|
18
|
+
]
|
19
|
+
dependencies = [
|
20
|
+
"requests>=2.25.0",
|
21
|
+
]
|
22
|
+
|
23
|
+
[project.urls]
|
24
|
+
Homepage = "https://github.com/shiventi/sparq"
|
25
|
+
|
26
|
+
[tool.setuptools]
|
27
|
+
py-modules = ["client", "auth", "recover", "usage", "sparq"]
|
sparq-0.1.0/recover.py
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
|
3
|
+
import requests
|
4
|
+
from pathlib import Path
|
5
|
+
|
6
|
+
API_URL = "https://sparq-api.onrender.com"
|
7
|
+
|
8
|
+
def save_api_key(api_key, email):
|
9
|
+
config_dir = Path.home() / ".sparq"
|
10
|
+
config_dir.mkdir(exist_ok=True)
|
11
|
+
config_file = config_dir / "config.txt"
|
12
|
+
|
13
|
+
with open(config_file, "w") as f:
|
14
|
+
f.write(f"API_KEY={api_key}\n")
|
15
|
+
f.write(f"EMAIL={email}\n")
|
16
|
+
|
17
|
+
print(f"\nAPI key saved to {config_file}")
|
18
|
+
|
19
|
+
def recover():
|
20
|
+
email = input("Email: ").strip()
|
21
|
+
|
22
|
+
if not email or "@" not in email:
|
23
|
+
print("Invalid email address")
|
24
|
+
return
|
25
|
+
|
26
|
+
response = requests.post(
|
27
|
+
f"{API_URL}/recover",
|
28
|
+
json={"email": email},
|
29
|
+
headers={"Content-Type": "application/json"}
|
30
|
+
)
|
31
|
+
|
32
|
+
if response.status_code != 200:
|
33
|
+
print(response.json()['detail'])
|
34
|
+
return
|
35
|
+
|
36
|
+
print("Verification code sent to your email")
|
37
|
+
|
38
|
+
code = input("Enter code: ").strip()
|
39
|
+
|
40
|
+
response = requests.post(
|
41
|
+
f"{API_URL}/recover",
|
42
|
+
json={"email": email, "code": code},
|
43
|
+
headers={"Content-Type": "application/json"}
|
44
|
+
)
|
45
|
+
|
46
|
+
if response.status_code == 200:
|
47
|
+
api_key = response.json()["api_key"]
|
48
|
+
print(f"\nAPI Key: {api_key}")
|
49
|
+
save_api_key(api_key, email)
|
50
|
+
else:
|
51
|
+
print(response.json()['detail'])
|
52
|
+
|
53
|
+
if __name__ == "__main__":
|
54
|
+
recover()
|
sparq-0.1.0/setup.cfg
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
Metadata-Version: 2.4
|
2
|
+
Name: sparq
|
3
|
+
Version: 0.1.0
|
4
|
+
Summary: Python client for the sparq api - automated degree planning for SJSU students + more
|
5
|
+
Author: Shiven Sheth
|
6
|
+
License: MIT
|
7
|
+
Project-URL: Homepage, https://github.com/shiventi/sparq
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
9
|
+
Classifier: Operating System :: OS Independent
|
10
|
+
Requires-Python: >=3.8
|
11
|
+
Description-Content-Type: text/markdown
|
12
|
+
License-File: LICENSE
|
13
|
+
Requires-Dist: requests>=2.25.0
|
14
|
+
Dynamic: license-file
|
15
|
+
|
16
|
+
# sparq Client
|
17
|
+
|
18
|
+
Python client library for the sparq API - automated degree planning for SJSU students.
|
19
|
+
|
20
|
+
## Installation
|
21
|
+
|
22
|
+
```bash
|
23
|
+
pip install sparq
|
@@ -0,0 +1 @@
|
|
1
|
+
|
@@ -0,0 +1 @@
|
|
1
|
+
requests>=2.25.0
|
sparq-0.1.0/sparq.py
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
from client import Sparq
|
2
|
+
|
3
|
+
client = Sparq("your-api-key-here")
|
4
|
+
|
5
|
+
# edit accordingly
|
6
|
+
plan = client.plan(
|
7
|
+
major= "Computer Science",
|
8
|
+
cc_courses= [
|
9
|
+
# EVC Courses (based on actual transcript)
|
10
|
+
{"code": "COMSC 075", "title": "Computer Science I", "grade": "A", "institution": "Evergreen Valley College"},
|
11
|
+
{"code": "COMSC 076", "title": "Computer Science II", "grade": "A", "institution": "Evergreen Valley College"},
|
12
|
+
{"code": "COMS 020", "title": "Oral Communication", "grade": "A", "institution": "Evergreen Valley College"},
|
13
|
+
{"code": "ART 096", "title": "History of Asian Art", "grade": "A", "institution": "Evergreen Valley College"},
|
14
|
+
{"code": "COMS 035", "title": "Intercultural Communication", "grade": "A", "institution": "Evergreen Valley College"},
|
15
|
+
{"code": "PSYCH 001", "title": "General Psychology", "grade": "A", "institution": "Evergreen Valley College"},
|
16
|
+
{"code": "PHIL 060", "title": "Logic and Critical Thinking", "grade": "A", "institution": "Evergreen Valley College"},
|
17
|
+
{"code": "PHIL 010", "title": "Introduction to Philosophy", "grade": "A", "institution": "Evergreen Valley College"},
|
18
|
+
{"code": "PHIL 065", "title": "Introduction to Ethics", "grade": "A", "institution": "Evergreen Valley College"},
|
19
|
+
{"code": "COMSC 080", "title": "Discrete Structures", "grade": "A", "institution": "Evergreen Valley College"},
|
20
|
+
{"code": "HIST 017A", "title": "History of the United States", "grade": "A", "institution": "Evergreen Valley College"},
|
21
|
+
# SJCC Courses
|
22
|
+
{"code": "ENGL 001A", "title": "English Composition", "grade": "A", "institution": "San Jose City College"},
|
23
|
+
],
|
24
|
+
ap_exams= [
|
25
|
+
{"test": "Calculus AB", "score": 5},
|
26
|
+
{"test": "Calculus BC", "score": 4},
|
27
|
+
{"test": "World History", "score": 4},
|
28
|
+
{"test": "Physics C, Mechanics", "score": 4},
|
29
|
+
],
|
30
|
+
sjsu_courses= [
|
31
|
+
{"code": "MATH 32", "title": "Calculus III", "status": "In Progress", "term": "Fall 2025"},
|
32
|
+
{"code": "CS 49J", "title": "Programming in Java", "status": "In Progress", "term": "Fall 2025"},
|
33
|
+
{"code": "NUFS 16", "title": "Nutrition", "status": "In Progress", "term": "Fall 2025"},
|
34
|
+
{"code": "METR 10", "title": "Weather and Climate", "status": "In Progress", "term": "Fall 2025"},
|
35
|
+
],
|
36
|
+
units_per_semester= 15
|
37
|
+
)
|
38
|
+
|
39
|
+
print(plan)
|
sparq-0.1.0/usage.py
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
import requests
|
2
|
+
from pathlib import Path
|
3
|
+
|
4
|
+
API_URL = "http://localhost:8000"
|
5
|
+
|
6
|
+
def load_api_key():
|
7
|
+
config_path = Path.home() / ".sparq" / "config.txt"
|
8
|
+
if not config_path.exists():
|
9
|
+
return None
|
10
|
+
with open(config_path, "r") as f:
|
11
|
+
for line in f:
|
12
|
+
if line.startswith("API_KEY="):
|
13
|
+
return line.strip().split("=", 1)[1]
|
14
|
+
return None
|
15
|
+
|
16
|
+
def view_usage(api_key=None):
|
17
|
+
if not api_key:
|
18
|
+
api_key = load_api_key()
|
19
|
+
if not api_key:
|
20
|
+
print("No API key found. Please run auth.py to register first.")
|
21
|
+
return
|
22
|
+
|
23
|
+
try:
|
24
|
+
response = requests.get(
|
25
|
+
f"{API_URL}/usage",
|
26
|
+
headers={"x-api-key": api_key}
|
27
|
+
)
|
28
|
+
|
29
|
+
if response.status_code == 200:
|
30
|
+
data = response.json()
|
31
|
+
|
32
|
+
print(f"\nEmail: {data['email']}")
|
33
|
+
print(f"Total API Calls: {data['total_api_calls']}\n")
|
34
|
+
|
35
|
+
if data['recent_calls']:
|
36
|
+
print("Recent Calls:")
|
37
|
+
for i, call in enumerate(data['recent_calls'], 1):
|
38
|
+
major = call.get('request_data', {}).get('major', 'N/A') if call.get('request_data') else 'N/A'
|
39
|
+
print(f" {i}. {call['endpoint']} | {call['response_status']} | {major} | {call['created_at']}")
|
40
|
+
else:
|
41
|
+
print("No API calls recorded yet.\n")
|
42
|
+
|
43
|
+
elif response.status_code == 401:
|
44
|
+
print("Invalid or inactive API key")
|
45
|
+
else:
|
46
|
+
print(f"Error: {response.json().get('detail', 'Unknown error')}")
|
47
|
+
|
48
|
+
except requests.exceptions.ConnectionError:
|
49
|
+
print(f"Cannot connect to API at {API_URL}")
|
50
|
+
print("Make sure the server is running")
|
51
|
+
except Exception as e:
|
52
|
+
print(f"Error: {e}")
|
53
|
+
|
54
|
+
if __name__ == "__main__":
|
55
|
+
view_usage()
|