Compare commits

...

6 Commits

Author SHA1 Message Date
Boyan
1a950c0eb2 Update README 2024-11-17 22:19:50 +01:00
Boyan
a3a9f5dfee Updated index 2024-11-17 22:06:06 +01:00
Boyan
461b506be5 Added requirements 2024-11-17 21:57:33 +01:00
Boyan
a0aeca87b1 Updated changelog and doc with new password storage method 2024-11-17 21:57:07 +01:00
Boyan
17f0bd237a Solved #11 by using system's keyring 2024-11-17 21:53:02 +01:00
Boyan
2378be4d42 Fixed slight bug with unused method 2024-11-17 21:52:11 +01:00
7 changed files with 54 additions and 15 deletions

View File

@ -1,12 +1,12 @@
<p align="center"> <p align="center">
<img src="docs/img/rugemmie.gif" /> <img src="https://github.com/Code-For-Groningen/temmies/blob/v1.1.0/docs/img/rugemmie.gif" />
</p> </p>
<p align="center"> <p align="center">
<a href="https://temmies.confest.im"><img alt="Read the Docs" src="https://img.shields.io/readthedocs/temmies"></a> <a href="https://temmies.confest.im"><img alt="Read the Docs" src="https://img.shields.io/readthedocs/temmies"></a>
<img alt="GitHub" src="https://img.shields.io/github/license/Code-For-Groningen/temmies"> <img alt="GitHub" src="https://img.shields.io/github/license/Code-For-Groningen/temmies">
</p> </p>
A python library which interacts with themis. Uses bs4. I'll try to end development on a somewhat working state. A python library which interacts with [Themis](https://themis.housing.rug.nl/). Uses bs4. I'll try to end development on a somewhat working state.
## Intended Features ## Intended Features
* [x] Log in * [x] Log in
@ -15,11 +15,11 @@ A python library which interacts with themis. Uses bs4. I'll try to end developm
* [x] Submission status * [x] Submission status
## Docs ## Docs
[here](http://temmies.confest.im/). - [here](http://temmies.confest.im/).
## Possible continuations ## Possible continuations
* Discord bot * Discord bot
* CLI program * [CLI program](https://github.com/Code-For-Groningen/temmies-cli)
## Thanks to ## Thanks to
* [Glitchcat](https://glitchcat.github.io/themis-api/), cool docs bro. * [Glitchcat](https://glitchcat.github.io/themis-api/), cool docs bro.

View File

@ -8,9 +8,11 @@ Creates the initial connection to Themis.
```python ```python
from temmies.themis import Themis from temmies.themis import Themis
themis = Themis("s-number", "password") themis = Themis("s-number")
``` ```
On the first run, you will be prompted for your password. Then, on the next run(s), you will be able to log in automatically, as the password is stored in the system keyring. If you want to delete it [click here](https://www.google.com/search?hl=en&q=delete%20a%20password%20from%20keyring).
### Methods ### Methods
#### `login()` #### `login()`
Logs in to Themis. Runs automatically when the class is initialized. Logs in to Themis. Runs automatically when the class is initialized.

View File

@ -11,4 +11,4 @@
#### **Codebase** #### **Codebase**
- Prepended `get_` to all methods in `Submission` - Prepended `get_` to all methods in `Submission`
- Created base `Group` from which `Course` and `ExerciseGroup` inherit. - Created base `Group` from which `Course` and `ExerciseGroup` inherit.
- - Using system keyring to store passwords (Issue #11)

View File

@ -21,7 +21,7 @@ pip install temmies
from temmies.themis import Themis from temmies.themis import Themis
# Log in # Log in
themis = Themis("s-number", "password") themis = Themis("s-number") # You will be prompted for your password
# Get a year # Get a year
year = themis.get_year(2023, 2024) year = themis.get_year(2023, 2024)

19
requirements.txt Normal file
View File

@ -0,0 +1,19 @@
beautifulsoup4==4.12.3
bs4==0.0.2
certifi==2024.8.30
cffi==1.17.1
charset-normalizer==3.4.0
cryptography==43.0.3
idna==3.10
jaraco.classes==3.4.0
jaraco.context==6.0.1
jaraco.functools==4.1.0
jeepney==0.8.0
keyring==25.5.0
lxml==5.3.0
more-itertools==10.5.0
pycparser==2.22
requests==2.32.3
SecretStorage==3.3.3
soupsieve==2.6
urllib3==2.2.3

View File

@ -79,6 +79,6 @@ class Submission:
def get_files(self) -> list[str] | None: def get_files(self) -> list[str] | None:
"""Get a list of uploaded files in the format [(name, url)]""" """Get a list of uploaded files in the format [(name, url)]"""
if not self.__info: if not self.__info:
self.__info = self.info() self.__info = self.get_info()
return self.__info.get("files", None) return self.__info.get("files", None)

View File

@ -4,6 +4,8 @@ Main class for the Themis API
""" """
import urllib3 import urllib3
import keyring
import getpass
from requests import Session from requests import Session
from bs4 import BeautifulSoup from bs4 import BeautifulSoup
from .year import Year from .year import Year
@ -21,10 +23,22 @@ class Themis:
all_years: Get all years all_years: Get all years
""" """
def __init__(self, user: str, passwd: str): def __init__(self, user: str):
self.session = self.login(user, passwd) self.user = user
self.years = [] self.password = self.__get_password()
self.url = "https://themis.housing.rug.nl/course/" self.session = self.login(user, self.password)
def __get_password(self) -> str:
"""
Retrieve the password from the keyring, prompting the user if not found.
"""
password = keyring.get_password(f'{self.user}-temmies', self.user)
if not password:
print(f"Password for user '{self.user}' not found in keyring.")
password = getpass.getpass(prompt=f"Enter password for {self.user}: ")
keyring.set_password(f'{self.user}-temmies', self.user, password)
print("Password saved securely in keyring.")
return password
def login(self, user: str, passwd: str) -> Session: def login(self, user: str, passwd: str) -> Session:
""" """
@ -58,9 +72,13 @@ class Themis:
# check if login was successful # check if login was successful
log_out = "Welcome, logged in as" in r.text log_out = "Welcome, logged in as" in r.text
if not log_out: if "Invalid credentials" in r.text:
raise IllegalAction(message=f"Login for user {user} failed") # Prompt for password again
print("Invalid credentials. Please try again.")
passwd = getpass.getpass(prompt="Enter password: ")
keyring.set_password(f'{self.user}-temmies', self.user, passwd)
return self.login(user, passwd)
return s return s
def get_year(self, start: int, end: int) -> Year: def get_year(self, start: int, end: int) -> Year: