studentpybox 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.
- studentpybox-0.1.0/LICENSE +21 -0
- studentpybox-0.1.0/PKG-INFO +61 -0
- studentpybox-0.1.0/README.md +44 -0
- studentpybox-0.1.0/pyproject.toml +28 -0
- studentpybox-0.1.0/setup.cfg +4 -0
- studentpybox-0.1.0/src/studentpybox/__init__.py +36 -0
- studentpybox-0.1.0/src/studentpybox/gpa.py +166 -0
- studentpybox-0.1.0/src/studentpybox/study.py +143 -0
- studentpybox-0.1.0/src/studentpybox.egg-info/PKG-INFO +61 -0
- studentpybox-0.1.0/src/studentpybox.egg-info/SOURCES.txt +11 -0
- studentpybox-0.1.0/src/studentpybox.egg-info/dependency_links.txt +1 -0
- studentpybox-0.1.0/src/studentpybox.egg-info/top_level.txt +1 -0
- studentpybox-0.1.0/tests/test_studentpybox.py +60 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Talha_main
|
|
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.
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: studentpybox
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A simple Python toolkit for university students: GPA tools and study planning helpers.
|
|
5
|
+
Author-email: Talha_main <450talhatahir@gmail.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/YOUR_GITHUB_USERNAME/studentpybox
|
|
8
|
+
Keywords: gpa,student,university,study,pomodoro,education
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Classifier: Operating System :: OS Independent
|
|
11
|
+
Classifier: Intended Audience :: Education
|
|
12
|
+
Classifier: Topic :: Education
|
|
13
|
+
Requires-Python: >=3.8
|
|
14
|
+
Description-Content-Type: text/markdown
|
|
15
|
+
License-File: LICENSE
|
|
16
|
+
Dynamic: license-file
|
|
17
|
+
|
|
18
|
+
# studentpybox
|
|
19
|
+
|
|
20
|
+
A simple Python toolkit for university students, with two modules: **GPA tools** and **study planning helpers**.
|
|
21
|
+
|
|
22
|
+
## Installation
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
pip install studentpybox
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Modules & Functions
|
|
29
|
+
|
|
30
|
+
### `studentpybox.gpa`
|
|
31
|
+
|
|
32
|
+
| Function | Description |
|
|
33
|
+
|---|---|
|
|
34
|
+
| `calculate_gpa(grades, credits)` | Credit-weighted GPA from a list of grade points and credit hours. |
|
|
35
|
+
| `percentage_to_grade(percentage)` | Convert a percentage score into a letter grade. |
|
|
36
|
+
| `cgpa_predictor(current_cgpa, completed_credits, target_cgpa, upcoming_credits)` | GPA needed in upcoming courses to hit a target CGPA. |
|
|
37
|
+
| `attendance_needed(attended, total, target_percentage=75.0)` | Classes still needed (or classes that can be skipped) to meet an attendance target. |
|
|
38
|
+
|
|
39
|
+
### `studentpybox.study`
|
|
40
|
+
|
|
41
|
+
| Function | Description |
|
|
42
|
+
|---|---|
|
|
43
|
+
| `pomodoro_timer_text(work_minutes=25, break_minutes=5, sessions=4)` | Printable Pomodoro study schedule. |
|
|
44
|
+
| `random_motivation()` | A random short motivational line. |
|
|
45
|
+
| `study_hours_left(total_hours_needed, hours_completed)` | Remaining study hours and progress percentage. |
|
|
46
|
+
| `exam_countdown(exam_date, date_format="%Y-%m-%d")` | Days left until an exam date. |
|
|
47
|
+
|
|
48
|
+
## Usage
|
|
49
|
+
|
|
50
|
+
```python
|
|
51
|
+
from studentpybox import calculate_gpa, percentage_to_grade, random_motivation, exam_countdown
|
|
52
|
+
|
|
53
|
+
print(calculate_gpa([4.0, 3.3, 3.7], [3, 4, 3])) # 3.63
|
|
54
|
+
print(percentage_to_grade(82)) # B+
|
|
55
|
+
print(random_motivation()) # a random study quote
|
|
56
|
+
print(exam_countdown("2026-07-15")) # {'days_left': ..., 'status': 'upcoming'}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## License
|
|
60
|
+
|
|
61
|
+
MIT
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# studentpybox
|
|
2
|
+
|
|
3
|
+
A simple Python toolkit for university students, with two modules: **GPA tools** and **study planning helpers**.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pip install studentpybox
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Modules & Functions
|
|
12
|
+
|
|
13
|
+
### `studentpybox.gpa`
|
|
14
|
+
|
|
15
|
+
| Function | Description |
|
|
16
|
+
|---|---|
|
|
17
|
+
| `calculate_gpa(grades, credits)` | Credit-weighted GPA from a list of grade points and credit hours. |
|
|
18
|
+
| `percentage_to_grade(percentage)` | Convert a percentage score into a letter grade. |
|
|
19
|
+
| `cgpa_predictor(current_cgpa, completed_credits, target_cgpa, upcoming_credits)` | GPA needed in upcoming courses to hit a target CGPA. |
|
|
20
|
+
| `attendance_needed(attended, total, target_percentage=75.0)` | Classes still needed (or classes that can be skipped) to meet an attendance target. |
|
|
21
|
+
|
|
22
|
+
### `studentpybox.study`
|
|
23
|
+
|
|
24
|
+
| Function | Description |
|
|
25
|
+
|---|---|
|
|
26
|
+
| `pomodoro_timer_text(work_minutes=25, break_minutes=5, sessions=4)` | Printable Pomodoro study schedule. |
|
|
27
|
+
| `random_motivation()` | A random short motivational line. |
|
|
28
|
+
| `study_hours_left(total_hours_needed, hours_completed)` | Remaining study hours and progress percentage. |
|
|
29
|
+
| `exam_countdown(exam_date, date_format="%Y-%m-%d")` | Days left until an exam date. |
|
|
30
|
+
|
|
31
|
+
## Usage
|
|
32
|
+
|
|
33
|
+
```python
|
|
34
|
+
from studentpybox import calculate_gpa, percentage_to_grade, random_motivation, exam_countdown
|
|
35
|
+
|
|
36
|
+
print(calculate_gpa([4.0, 3.3, 3.7], [3, 4, 3])) # 3.63
|
|
37
|
+
print(percentage_to_grade(82)) # B+
|
|
38
|
+
print(random_motivation()) # a random study quote
|
|
39
|
+
print(exam_countdown("2026-07-15")) # {'days_left': ..., 'status': 'upcoming'}
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## License
|
|
43
|
+
|
|
44
|
+
MIT
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68.0", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "studentpybox"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "A simple Python toolkit for university students: GPA tools and study planning helpers."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.8"
|
|
11
|
+
license = "MIT"
|
|
12
|
+
license-files = ["LICENSE"]
|
|
13
|
+
authors = [
|
|
14
|
+
{ name = "Talha_main", email = "450talhatahir@gmail.com" }
|
|
15
|
+
]
|
|
16
|
+
keywords = ["gpa", "student", "university", "study", "pomodoro", "education"]
|
|
17
|
+
classifiers = [
|
|
18
|
+
"Programming Language :: Python :: 3",
|
|
19
|
+
"Operating System :: OS Independent",
|
|
20
|
+
"Intended Audience :: Education",
|
|
21
|
+
"Topic :: Education",
|
|
22
|
+
]
|
|
23
|
+
|
|
24
|
+
[project.urls]
|
|
25
|
+
Homepage = "https://github.com/YOUR_GITHUB_USERNAME/studentpybox"
|
|
26
|
+
|
|
27
|
+
[tool.setuptools.packages.find]
|
|
28
|
+
where = ["src"]
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"""
|
|
2
|
+
studentpybox - A simple Python toolkit for university students.
|
|
3
|
+
|
|
4
|
+
Modules:
|
|
5
|
+
gpa - GPA calculation, grading, and attendance helpers.
|
|
6
|
+
study - Study planning and motivation helpers.
|
|
7
|
+
|
|
8
|
+
All functions are also importable directly from the top-level package, e.g.:
|
|
9
|
+
from studentpybox import calculate_gpa, random_motivation
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from .gpa import (
|
|
13
|
+
calculate_gpa,
|
|
14
|
+
percentage_to_grade,
|
|
15
|
+
cgpa_predictor,
|
|
16
|
+
attendance_needed,
|
|
17
|
+
)
|
|
18
|
+
from .study import (
|
|
19
|
+
pomodoro_timer_text,
|
|
20
|
+
random_motivation,
|
|
21
|
+
study_hours_left,
|
|
22
|
+
exam_countdown,
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
__version__ = "0.1.0"
|
|
26
|
+
|
|
27
|
+
__all__ = [
|
|
28
|
+
"calculate_gpa",
|
|
29
|
+
"percentage_to_grade",
|
|
30
|
+
"cgpa_predictor",
|
|
31
|
+
"attendance_needed",
|
|
32
|
+
"pomodoro_timer_text",
|
|
33
|
+
"random_motivation",
|
|
34
|
+
"study_hours_left",
|
|
35
|
+
"exam_countdown",
|
|
36
|
+
]
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
"""
|
|
2
|
+
gpa.py - GPA, grading, and attendance helpers for university students.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
# Standard 4.0-scale grade boundaries used by percentage_to_grade().
|
|
8
|
+
# Each tuple is (minimum percentage, letter grade, grade point).
|
|
9
|
+
_GRADE_TABLE = [
|
|
10
|
+
(90, "A", 4.0),
|
|
11
|
+
(85, "A-", 3.7),
|
|
12
|
+
(80, "B+", 3.3),
|
|
13
|
+
(75, "B", 3.0),
|
|
14
|
+
(70, "B-", 2.7),
|
|
15
|
+
(65, "C+", 2.3),
|
|
16
|
+
(60, "C", 2.0),
|
|
17
|
+
(55, "C-", 1.7),
|
|
18
|
+
(50, "D", 1.0),
|
|
19
|
+
(0, "F", 0.0),
|
|
20
|
+
]
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def calculate_gpa(grades: list[float], credits: list[float]) -> float:
|
|
24
|
+
"""
|
|
25
|
+
Calculate a credit-weighted GPA.
|
|
26
|
+
|
|
27
|
+
Args:
|
|
28
|
+
grades: Grade points earned per course (e.g. [4.0, 3.3, 3.7]).
|
|
29
|
+
credits: Credit hours for each course, same order as grades.
|
|
30
|
+
|
|
31
|
+
Returns:
|
|
32
|
+
The weighted GPA rounded to 2 decimal places.
|
|
33
|
+
|
|
34
|
+
Example:
|
|
35
|
+
>>> calculate_gpa([4.0, 3.3, 3.7], [3, 4, 3])
|
|
36
|
+
3.63
|
|
37
|
+
"""
|
|
38
|
+
if not grades or not credits:
|
|
39
|
+
raise ValueError("grades and credits cannot be empty")
|
|
40
|
+
if len(grades) != len(credits):
|
|
41
|
+
raise ValueError("grades and credits must have the same length")
|
|
42
|
+
if any(c <= 0 for c in credits):
|
|
43
|
+
raise ValueError("credits must be positive numbers")
|
|
44
|
+
|
|
45
|
+
total_points = sum(g * c for g, c in zip(grades, credits))
|
|
46
|
+
total_credits = sum(credits)
|
|
47
|
+
return round(total_points / total_credits, 2)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def percentage_to_grade(percentage: float) -> str:
|
|
51
|
+
"""
|
|
52
|
+
Convert a percentage score into a letter grade.
|
|
53
|
+
|
|
54
|
+
Args:
|
|
55
|
+
percentage: Score between 0 and 100.
|
|
56
|
+
|
|
57
|
+
Returns:
|
|
58
|
+
A letter grade, e.g. "A", "B+", "C-", "F".
|
|
59
|
+
|
|
60
|
+
Example:
|
|
61
|
+
>>> percentage_to_grade(82)
|
|
62
|
+
'B+'
|
|
63
|
+
"""
|
|
64
|
+
if not 0 <= percentage <= 100:
|
|
65
|
+
raise ValueError("percentage must be between 0 and 100")
|
|
66
|
+
|
|
67
|
+
for minimum, letter, _ in _GRADE_TABLE:
|
|
68
|
+
if percentage >= minimum:
|
|
69
|
+
return letter
|
|
70
|
+
return "F" # unreachable, but keeps type-checkers happy
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def cgpa_predictor(
|
|
74
|
+
current_cgpa: float,
|
|
75
|
+
completed_credits: float,
|
|
76
|
+
target_cgpa: float,
|
|
77
|
+
upcoming_credits: float,
|
|
78
|
+
) -> float:
|
|
79
|
+
"""
|
|
80
|
+
Estimate the GPA needed in upcoming courses to reach a target CGPA.
|
|
81
|
+
|
|
82
|
+
Args:
|
|
83
|
+
current_cgpa: CGPA earned so far.
|
|
84
|
+
completed_credits: Total credit hours completed so far.
|
|
85
|
+
target_cgpa: The CGPA the student wants to reach.
|
|
86
|
+
upcoming_credits: Credit hours still to be taken.
|
|
87
|
+
|
|
88
|
+
Returns:
|
|
89
|
+
The GPA required in the upcoming credits, rounded to 2 decimals.
|
|
90
|
+
Can exceed 4.0 (or whatever your scale's max is) if the target
|
|
91
|
+
is no longer mathematically reachable -- the caller should
|
|
92
|
+
compare the result against their grading scale's maximum.
|
|
93
|
+
|
|
94
|
+
Example:
|
|
95
|
+
>>> cgpa_predictor(3.2, 60, 3.5, 20)
|
|
96
|
+
4.4
|
|
97
|
+
"""
|
|
98
|
+
if completed_credits <= 0 or upcoming_credits <= 0:
|
|
99
|
+
raise ValueError("credits must be positive numbers")
|
|
100
|
+
|
|
101
|
+
total_credits = completed_credits + upcoming_credits
|
|
102
|
+
required_total_points = target_cgpa * total_credits
|
|
103
|
+
current_points = current_cgpa * completed_credits
|
|
104
|
+
required_gpa = (required_total_points - current_points) / upcoming_credits
|
|
105
|
+
return round(required_gpa, 2)
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def attendance_needed(
|
|
109
|
+
attended: int, total: int, target_percentage: float = 75.0
|
|
110
|
+
) -> dict:
|
|
111
|
+
"""
|
|
112
|
+
Work out attendance status against a target percentage.
|
|
113
|
+
|
|
114
|
+
Args:
|
|
115
|
+
attended: Classes attended so far.
|
|
116
|
+
total: Total classes held so far.
|
|
117
|
+
target_percentage: Required attendance percentage (default 75%).
|
|
118
|
+
|
|
119
|
+
Returns:
|
|
120
|
+
A dict with keys:
|
|
121
|
+
"current_percentage": current attendance percentage,
|
|
122
|
+
"status": "safe" or "short",
|
|
123
|
+
"classes_to_attend": consecutive classes still needed to
|
|
124
|
+
reach the target (0 if already met),
|
|
125
|
+
"classes_can_skip": consecutive classes that can still be
|
|
126
|
+
missed while staying at/above the target (0 if short).
|
|
127
|
+
|
|
128
|
+
Example:
|
|
129
|
+
>>> attendance_needed(28, 40, 75)
|
|
130
|
+
{'current_percentage': 70.0, 'status': 'short', 'classes_to_attend': 8, 'classes_can_skip': 0}
|
|
131
|
+
"""
|
|
132
|
+
if total <= 0:
|
|
133
|
+
raise ValueError("total must be greater than 0")
|
|
134
|
+
if attended < 0 or attended > total:
|
|
135
|
+
raise ValueError("attended must be between 0 and total")
|
|
136
|
+
if not 0 < target_percentage <= 100:
|
|
137
|
+
raise ValueError("target_percentage must be between 0 and 100")
|
|
138
|
+
|
|
139
|
+
current_percentage = round((attended / total) * 100, 2)
|
|
140
|
+
|
|
141
|
+
if current_percentage >= target_percentage:
|
|
142
|
+
# How many more classes (all missed) could the student skip
|
|
143
|
+
# before dropping below the target?
|
|
144
|
+
can_skip = 0
|
|
145
|
+
while (attended) / (total + can_skip + 1) * 100 >= target_percentage:
|
|
146
|
+
can_skip += 1
|
|
147
|
+
return {
|
|
148
|
+
"current_percentage": current_percentage,
|
|
149
|
+
"status": "safe",
|
|
150
|
+
"classes_to_attend": 0,
|
|
151
|
+
"classes_can_skip": can_skip,
|
|
152
|
+
}
|
|
153
|
+
else:
|
|
154
|
+
# How many more classes (all attended) are needed to reach target?
|
|
155
|
+
need = 0
|
|
156
|
+
a, t = attended, total
|
|
157
|
+
while (a / t) * 100 < target_percentage:
|
|
158
|
+
a += 1
|
|
159
|
+
t += 1
|
|
160
|
+
need += 1
|
|
161
|
+
return {
|
|
162
|
+
"current_percentage": current_percentage,
|
|
163
|
+
"status": "short",
|
|
164
|
+
"classes_to_attend": need,
|
|
165
|
+
"classes_can_skip": 0,
|
|
166
|
+
}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
"""
|
|
2
|
+
study.py - Study planning and motivation helpers for university students.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
import random
|
|
8
|
+
from datetime import datetime, date
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
# Original, short motivational lines (no external/copyrighted quotes).
|
|
12
|
+
_MOTIVATION_LINES = [
|
|
13
|
+
"Small steps every day add up to big results. Open the book.",
|
|
14
|
+
"You don't need to feel motivated to start -- starting creates the motivation.",
|
|
15
|
+
"Future you is counting on the work you put in right now.",
|
|
16
|
+
"Progress, not perfection. One page at a time.",
|
|
17
|
+
"The hardest part is opening the laptop. You've already done it.",
|
|
18
|
+
"Tired brains still learn -- just take it slow and keep going.",
|
|
19
|
+
"Every concept you understand today is one less thing to fear at exam time.",
|
|
20
|
+
"You've survived 100% of your hard days so far. This one's no different.",
|
|
21
|
+
"Done is better than perfect. Submit the draft, polish later.",
|
|
22
|
+
"Your only competition is who you were yesterday.",
|
|
23
|
+
]
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def pomodoro_timer_text(
|
|
27
|
+
work_minutes: int = 25, break_minutes: int = 5, sessions: int = 4
|
|
28
|
+
) -> str:
|
|
29
|
+
"""
|
|
30
|
+
Generate a printable Pomodoro study schedule.
|
|
31
|
+
|
|
32
|
+
Args:
|
|
33
|
+
work_minutes: Length of each focused work block (default 25).
|
|
34
|
+
break_minutes: Length of each short break (default 5).
|
|
35
|
+
sessions: Number of work/break cycles (default 4).
|
|
36
|
+
|
|
37
|
+
Returns:
|
|
38
|
+
A formatted multi-line schedule string, e.g.:
|
|
39
|
+
"Session 1: Study 25 min -> Break 5 min
|
|
40
|
+
Session 2: Study 25 min -> Break 5 min
|
|
41
|
+
...
|
|
42
|
+
Total focused study time: 100 min"
|
|
43
|
+
|
|
44
|
+
Example:
|
|
45
|
+
>>> print(pomodoro_timer_text(25, 5, 2))
|
|
46
|
+
Session 1: Study 25 min -> Break 5 min
|
|
47
|
+
Session 2: Study 25 min -> Break 5 min
|
|
48
|
+
Total focused study time: 50 min
|
|
49
|
+
"""
|
|
50
|
+
if work_minutes <= 0 or break_minutes <= 0 or sessions <= 0:
|
|
51
|
+
raise ValueError("work_minutes, break_minutes, and sessions must be positive")
|
|
52
|
+
|
|
53
|
+
lines = [
|
|
54
|
+
f"Session {i}: Study {work_minutes} min -> Break {break_minutes} min"
|
|
55
|
+
for i in range(1, sessions + 1)
|
|
56
|
+
]
|
|
57
|
+
lines.append(f"Total focused study time: {work_minutes * sessions} min")
|
|
58
|
+
return "\n".join(lines)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def random_motivation() -> str:
|
|
62
|
+
"""
|
|
63
|
+
Return a random, short motivational line for studying.
|
|
64
|
+
|
|
65
|
+
Returns:
|
|
66
|
+
A motivational sentence as a string.
|
|
67
|
+
|
|
68
|
+
Example:
|
|
69
|
+
>>> isinstance(random_motivation(), str)
|
|
70
|
+
True
|
|
71
|
+
"""
|
|
72
|
+
return random.choice(_MOTIVATION_LINES)
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def study_hours_left(total_hours_needed: float, hours_completed: float) -> dict:
|
|
76
|
+
"""
|
|
77
|
+
Track remaining study hours against a goal.
|
|
78
|
+
|
|
79
|
+
Args:
|
|
80
|
+
total_hours_needed: Total hours planned for a subject/exam.
|
|
81
|
+
hours_completed: Hours already studied.
|
|
82
|
+
|
|
83
|
+
Returns:
|
|
84
|
+
A dict with keys:
|
|
85
|
+
"hours_remaining": hours still left (never negative),
|
|
86
|
+
"percent_complete": progress percentage, rounded to 1 decimal,
|
|
87
|
+
"status": "done" if the goal is met, otherwise "in_progress".
|
|
88
|
+
|
|
89
|
+
Example:
|
|
90
|
+
>>> study_hours_left(20, 8)
|
|
91
|
+
{'hours_remaining': 12, 'percent_complete': 40.0, 'status': 'in_progress'}
|
|
92
|
+
"""
|
|
93
|
+
if total_hours_needed <= 0:
|
|
94
|
+
raise ValueError("total_hours_needed must be greater than 0")
|
|
95
|
+
if hours_completed < 0:
|
|
96
|
+
raise ValueError("hours_completed cannot be negative")
|
|
97
|
+
|
|
98
|
+
remaining = max(total_hours_needed - hours_completed, 0)
|
|
99
|
+
percent = round(min(hours_completed / total_hours_needed, 1) * 100, 1)
|
|
100
|
+
status = "done" if remaining == 0 else "in_progress"
|
|
101
|
+
|
|
102
|
+
return {
|
|
103
|
+
"hours_remaining": remaining,
|
|
104
|
+
"percent_complete": percent,
|
|
105
|
+
"status": status,
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def exam_countdown(exam_date: str, date_format: str = "%Y-%m-%d") -> dict:
|
|
110
|
+
"""
|
|
111
|
+
Calculate how much time is left until an exam.
|
|
112
|
+
|
|
113
|
+
Args:
|
|
114
|
+
exam_date: The exam date as a string, e.g. "2026-07-15".
|
|
115
|
+
date_format: The format exam_date is given in (default "%Y-%m-%d").
|
|
116
|
+
|
|
117
|
+
Returns:
|
|
118
|
+
A dict with keys:
|
|
119
|
+
"days_left": full days remaining (0 if today, negative if past),
|
|
120
|
+
"status": "upcoming", "today", or "past".
|
|
121
|
+
|
|
122
|
+
Example:
|
|
123
|
+
>>> exam_countdown("2099-01-01") # doctest: +SKIP
|
|
124
|
+
{'days_left': ..., 'status': 'upcoming'}
|
|
125
|
+
"""
|
|
126
|
+
try:
|
|
127
|
+
target = datetime.strptime(exam_date, date_format).date()
|
|
128
|
+
except ValueError as exc:
|
|
129
|
+
raise ValueError(
|
|
130
|
+
f"exam_date '{exam_date}' does not match format '{date_format}'"
|
|
131
|
+
) from exc
|
|
132
|
+
|
|
133
|
+
today = date.today()
|
|
134
|
+
days_left = (target - today).days
|
|
135
|
+
|
|
136
|
+
if days_left > 0:
|
|
137
|
+
status = "upcoming"
|
|
138
|
+
elif days_left == 0:
|
|
139
|
+
status = "today"
|
|
140
|
+
else:
|
|
141
|
+
status = "past"
|
|
142
|
+
|
|
143
|
+
return {"days_left": days_left, "status": status}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: studentpybox
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A simple Python toolkit for university students: GPA tools and study planning helpers.
|
|
5
|
+
Author-email: Talha_main <450talhatahir@gmail.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/YOUR_GITHUB_USERNAME/studentpybox
|
|
8
|
+
Keywords: gpa,student,university,study,pomodoro,education
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Classifier: Operating System :: OS Independent
|
|
11
|
+
Classifier: Intended Audience :: Education
|
|
12
|
+
Classifier: Topic :: Education
|
|
13
|
+
Requires-Python: >=3.8
|
|
14
|
+
Description-Content-Type: text/markdown
|
|
15
|
+
License-File: LICENSE
|
|
16
|
+
Dynamic: license-file
|
|
17
|
+
|
|
18
|
+
# studentpybox
|
|
19
|
+
|
|
20
|
+
A simple Python toolkit for university students, with two modules: **GPA tools** and **study planning helpers**.
|
|
21
|
+
|
|
22
|
+
## Installation
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
pip install studentpybox
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Modules & Functions
|
|
29
|
+
|
|
30
|
+
### `studentpybox.gpa`
|
|
31
|
+
|
|
32
|
+
| Function | Description |
|
|
33
|
+
|---|---|
|
|
34
|
+
| `calculate_gpa(grades, credits)` | Credit-weighted GPA from a list of grade points and credit hours. |
|
|
35
|
+
| `percentage_to_grade(percentage)` | Convert a percentage score into a letter grade. |
|
|
36
|
+
| `cgpa_predictor(current_cgpa, completed_credits, target_cgpa, upcoming_credits)` | GPA needed in upcoming courses to hit a target CGPA. |
|
|
37
|
+
| `attendance_needed(attended, total, target_percentage=75.0)` | Classes still needed (or classes that can be skipped) to meet an attendance target. |
|
|
38
|
+
|
|
39
|
+
### `studentpybox.study`
|
|
40
|
+
|
|
41
|
+
| Function | Description |
|
|
42
|
+
|---|---|
|
|
43
|
+
| `pomodoro_timer_text(work_minutes=25, break_minutes=5, sessions=4)` | Printable Pomodoro study schedule. |
|
|
44
|
+
| `random_motivation()` | A random short motivational line. |
|
|
45
|
+
| `study_hours_left(total_hours_needed, hours_completed)` | Remaining study hours and progress percentage. |
|
|
46
|
+
| `exam_countdown(exam_date, date_format="%Y-%m-%d")` | Days left until an exam date. |
|
|
47
|
+
|
|
48
|
+
## Usage
|
|
49
|
+
|
|
50
|
+
```python
|
|
51
|
+
from studentpybox import calculate_gpa, percentage_to_grade, random_motivation, exam_countdown
|
|
52
|
+
|
|
53
|
+
print(calculate_gpa([4.0, 3.3, 3.7], [3, 4, 3])) # 3.63
|
|
54
|
+
print(percentage_to_grade(82)) # B+
|
|
55
|
+
print(random_motivation()) # a random study quote
|
|
56
|
+
print(exam_countdown("2026-07-15")) # {'days_left': ..., 'status': 'upcoming'}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## License
|
|
60
|
+
|
|
61
|
+
MIT
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
README.md
|
|
3
|
+
pyproject.toml
|
|
4
|
+
src/studentpybox/__init__.py
|
|
5
|
+
src/studentpybox/gpa.py
|
|
6
|
+
src/studentpybox/study.py
|
|
7
|
+
src/studentpybox.egg-info/PKG-INFO
|
|
8
|
+
src/studentpybox.egg-info/SOURCES.txt
|
|
9
|
+
src/studentpybox.egg-info/dependency_links.txt
|
|
10
|
+
src/studentpybox.egg-info/top_level.txt
|
|
11
|
+
tests/test_studentpybox.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
studentpybox
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
from studentpybox import (
|
|
2
|
+
calculate_gpa,
|
|
3
|
+
percentage_to_grade,
|
|
4
|
+
cgpa_predictor,
|
|
5
|
+
attendance_needed,
|
|
6
|
+
pomodoro_timer_text,
|
|
7
|
+
random_motivation,
|
|
8
|
+
study_hours_left,
|
|
9
|
+
exam_countdown,
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def test_calculate_gpa():
|
|
14
|
+
assert calculate_gpa([4.0, 3.3, 3.7], [3, 4, 3]) == 3.63
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def test_percentage_to_grade():
|
|
18
|
+
assert percentage_to_grade(95) == "A"
|
|
19
|
+
assert percentage_to_grade(82) == "B+"
|
|
20
|
+
assert percentage_to_grade(40) == "F"
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def test_cgpa_predictor():
|
|
24
|
+
assert cgpa_predictor(3.2, 60, 3.5, 20) == 4.4
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def test_attendance_needed_short():
|
|
28
|
+
result = attendance_needed(28, 40, 75)
|
|
29
|
+
assert result["status"] == "short"
|
|
30
|
+
assert result["classes_to_attend"] == 8
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def test_attendance_needed_safe():
|
|
34
|
+
result = attendance_needed(38, 40, 75)
|
|
35
|
+
assert result["status"] == "safe"
|
|
36
|
+
assert result["classes_to_attend"] == 0
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def test_pomodoro_timer_text():
|
|
40
|
+
text = pomodoro_timer_text(25, 5, 2)
|
|
41
|
+
assert "Session 1" in text
|
|
42
|
+
assert "Total focused study time: 50 min" in text
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def test_random_motivation():
|
|
46
|
+
assert isinstance(random_motivation(), str)
|
|
47
|
+
assert len(random_motivation()) > 0
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def test_study_hours_left():
|
|
51
|
+
result = study_hours_left(20, 8)
|
|
52
|
+
assert result["hours_remaining"] == 12
|
|
53
|
+
assert result["percent_complete"] == 40.0
|
|
54
|
+
assert result["status"] == "in_progress"
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def test_exam_countdown_upcoming():
|
|
58
|
+
result = exam_countdown("2099-01-01")
|
|
59
|
+
assert result["status"] == "upcoming"
|
|
60
|
+
assert result["days_left"] > 0
|