This commit is contained in:
parent
c01ea3208a
commit
c9d4ef99af
17
README.md
17
README.md
@ -90,6 +90,23 @@ Streamlit 앱 실행:
|
||||
streamlit run src/app.py --server.port=20000
|
||||
```
|
||||
|
||||
### 인증 설정
|
||||
|
||||
애플리케이션은 streamlit-authenticator를 사용하여 로그인 기능을 제공합니다. 기본 계정은 다음과 같습니다:
|
||||
|
||||
- 관리자: 아이디 `admin`, 비밀번호 `adminpass`
|
||||
- 일반 사용자: 아이디 `user`, 비밀번호 `userpass`
|
||||
|
||||
비밀번호 변경 또는 계정 추가를 위한 steps:
|
||||
|
||||
1. `src/config/generate_credentials.py` 파일을 수정하여 원하는 계정 정보 입력
|
||||
2. 아래 명령어 실행:
|
||||
```
|
||||
cd src/config
|
||||
python generate_credentials.py
|
||||
```
|
||||
3. 생성된 `credentials.yaml` 파일을 확인하여 적용
|
||||
|
||||
## 모듈
|
||||
|
||||
### 데이터 수집
|
||||
|
||||
7
requirements.txt
Normal file
7
requirements.txt
Normal file
@ -0,0 +1,7 @@
|
||||
streamlit
|
||||
pandas
|
||||
pymysql
|
||||
sqlalchemy
|
||||
python-dotenv
|
||||
PyYAML
|
||||
streamlit-authenticator>=0.2.2
|
||||
82
src/app.py
82
src/app.py
@ -1,7 +1,11 @@
|
||||
"""
|
||||
Main Streamlit application for the Quant Manager.
|
||||
"""
|
||||
import os
|
||||
import yaml
|
||||
import streamlit as st
|
||||
import streamlit_authenticator as stauth
|
||||
from yaml.loader import SafeLoader
|
||||
from ui.pages.data_page import render_data_page
|
||||
from ui.pages.multi_factor_page import render_multi_factor_page
|
||||
from ui.pages.quality_page import render_quality_page
|
||||
@ -18,29 +22,69 @@ st.set_page_config(
|
||||
# Define the sidebar navigation
|
||||
def main():
|
||||
"""Main application function."""
|
||||
# Create sidebar navigation
|
||||
st.sidebar.title("콴트 매니저")
|
||||
# Load authentication configuration
|
||||
config_path = os.path.join(os.path.dirname(__file__), 'config', 'credentials.yaml')
|
||||
|
||||
# Navigation options
|
||||
pages = {
|
||||
"데이터 수집": render_data_page,
|
||||
"멀티 팩터 전략": render_multi_factor_page,
|
||||
"슈퍼 퀄리티 전략": render_quality_page,
|
||||
"슈퍼 밸류 모멘텀 전략": render_value_momentum_page
|
||||
}
|
||||
if os.path.exists(config_path):
|
||||
with open(config_path) as file:
|
||||
config = yaml.load(file, Loader=SafeLoader)
|
||||
|
||||
# Select page
|
||||
selection = st.sidebar.radio("메뉴", list(pages.keys()))
|
||||
# Create authenticator object
|
||||
authenticator = stauth.Authenticate(
|
||||
config['credentials'],
|
||||
config['cookie']['name'],
|
||||
config['cookie']['key'],
|
||||
config['cookie']['expiry_days'],
|
||||
)
|
||||
|
||||
# Render the selected page
|
||||
pages[selection]()
|
||||
# Initialize variables
|
||||
name = None
|
||||
|
||||
# Footer
|
||||
st.sidebar.markdown("---")
|
||||
st.sidebar.info(
|
||||
"© 2023-2025 콴트 매니저\n\n"
|
||||
"한국 주식 시장을 위한 퀀트 투자 도구"
|
||||
)
|
||||
# Create login widget with error handling
|
||||
try:
|
||||
authenticator.login()
|
||||
except Exception as e:
|
||||
st.error(f"로그인 처리 중 오류가 발생했습니다: {str(e)}")
|
||||
st.info("authenticator.login() 호출 문제가 발생했습니다. 구성 파일을 확인하세요.")
|
||||
|
||||
# Handle authentication status
|
||||
if st.session_state['authentication_status'] == False:
|
||||
st.error('아이디 또는 비밀번호가 올바르지 않습니다.')
|
||||
|
||||
if st.session_state['authentication_status'] is None:
|
||||
st.warning('로그인이 필요합니다.')
|
||||
|
||||
# Display application if authenticated
|
||||
if st.session_state['authentication_status']:
|
||||
# Create sidebar navigation
|
||||
st.sidebar.title(f"콴트 매니저 - {st.session_state["name"]}")
|
||||
|
||||
# Add logout button
|
||||
authenticator.logout('로그아웃', 'sidebar')
|
||||
|
||||
# Navigation options
|
||||
pages = {
|
||||
"데이터 수집": render_data_page,
|
||||
"멀티 팩터 전략": render_multi_factor_page,
|
||||
"슈퍼 퀄리티 전략": render_quality_page,
|
||||
"슈퍼 밸류 모멘텀 전략": render_value_momentum_page
|
||||
}
|
||||
|
||||
# Select page
|
||||
selection = st.sidebar.radio("메뉴", list(pages.keys()))
|
||||
|
||||
# Render the selected page
|
||||
pages[selection]()
|
||||
|
||||
# Footer
|
||||
st.sidebar.markdown("---")
|
||||
st.sidebar.info(
|
||||
"© 2023-2025 콴트 매니저\n\n"
|
||||
"한국 주식 시장을 위한 퀀트 투자 도구"
|
||||
)
|
||||
else:
|
||||
st.error("인증 설정 파일을 찾을 수 없습니다. config/credentials.yaml 파일을 확인하세요.")
|
||||
st.info(f"찾을 파일 경로: {config_path}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
10
src/config/credentials.yaml
Normal file
10
src/config/credentials.yaml
Normal file
@ -0,0 +1,10 @@
|
||||
cookie:
|
||||
expiry_days: 30
|
||||
key: 37A93A1A98AC9E896CBB21DD1F84FABC
|
||||
name: quant_manager_auth
|
||||
credentials:
|
||||
usernames:
|
||||
zephyrdark:
|
||||
email: zephyrdark@gamil.com
|
||||
name: ayuriel
|
||||
password: $2b$12$nqVU.Mw8lErQU59TxuGWLebfImar1O9zirjpc.waN/3LNYzc2o4.6
|
||||
51
src/config/generate_credentials.py
Normal file
51
src/config/generate_credentials.py
Normal file
@ -0,0 +1,51 @@
|
||||
"""
|
||||
Generate a credentials.yaml file with hashed passwords for streamlit-authenticator.
|
||||
This script should be run once to set up authentication credentials.
|
||||
"""
|
||||
import yaml
|
||||
import streamlit_authenticator as stauth
|
||||
from yaml.loader import SafeLoader
|
||||
|
||||
# Create credentials
|
||||
usernames = ["zephyrdark"]
|
||||
names = ["ayuriel"]
|
||||
passwords = [""] # Replace with secure passwords
|
||||
|
||||
# Hash passwords
|
||||
try:
|
||||
# For newer versions of streamlit-authenticator
|
||||
hashed_passwords = [stauth.Hasher([passwords[0]]).generate()[0]]
|
||||
except:
|
||||
# For older versions of streamlit-authenticator
|
||||
hashed_passwords = [stauth.Hasher().hash(passwords[0])]
|
||||
|
||||
# Create config dictionary
|
||||
config = {
|
||||
'credentials': {
|
||||
'usernames': {}
|
||||
},
|
||||
'cookie': {
|
||||
'name': 'quant_manager_auth',
|
||||
'key': '37A93A1A98AC9E896CBB21DD1F84FABC', # Key should be a string that is 32 bytes long
|
||||
'expiry_days': 30
|
||||
},
|
||||
'preauthorized': {
|
||||
'emails': []
|
||||
}
|
||||
}
|
||||
|
||||
# Add user credentials
|
||||
for i, username in enumerate(usernames):
|
||||
config['credentials']['usernames'][username] = {
|
||||
'name': names[i],
|
||||
'password': hashed_passwords[i],
|
||||
'email': f"{username}@gamil.com" # Replace with actual emails if needed
|
||||
}
|
||||
|
||||
# Write to YAML file
|
||||
with open('credentials.yaml', 'w') as file:
|
||||
yaml.dump(config, file, default_flow_style=False)
|
||||
|
||||
print("Credentials file has been generated successfully!")
|
||||
print("Update the usernames, passwords, and other information as needed.")
|
||||
print("Then move the credentials.yaml file to the config directory.")
|
||||
75
src/config/update_password.py
Normal file
75
src/config/update_password.py
Normal file
@ -0,0 +1,75 @@
|
||||
"""
|
||||
Update password for an existing user in credentials.yaml.
|
||||
"""
|
||||
import os
|
||||
import yaml
|
||||
import getpass
|
||||
import streamlit_authenticator as stauth
|
||||
from yaml.loader import SafeLoader
|
||||
|
||||
def main():
|
||||
# Get the path to credentials.yaml
|
||||
script_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
config_path = os.path.join(script_dir, 'credentials.yaml')
|
||||
|
||||
# Check if credentials file exists
|
||||
if not os.path.exists(config_path):
|
||||
print(f"Error: {config_path} not found.")
|
||||
return
|
||||
|
||||
# Load the credentials file
|
||||
with open(config_path, 'r') as file:
|
||||
config = yaml.load(file, Loader=SafeLoader)
|
||||
|
||||
# Get all usernames
|
||||
usernames = list(config['credentials']['usernames'].keys())
|
||||
|
||||
if not usernames:
|
||||
print("No users found in credentials file.")
|
||||
return
|
||||
|
||||
# Print available users
|
||||
print("Available users:")
|
||||
for i, username in enumerate(usernames, 1):
|
||||
print(f"{i}. {username}")
|
||||
|
||||
try:
|
||||
# Get user selection
|
||||
choice = int(input("\nSelect user number to update password: "))
|
||||
if choice < 1 or choice > len(usernames):
|
||||
print("Invalid selection.")
|
||||
return
|
||||
|
||||
selected_username = usernames[choice - 1]
|
||||
|
||||
# Get new password
|
||||
new_password = getpass.getpass("Enter new password: ")
|
||||
confirm_password = getpass.getpass("Confirm new password: ")
|
||||
|
||||
if new_password != confirm_password:
|
||||
print("Passwords do not match.")
|
||||
return
|
||||
|
||||
if not new_password:
|
||||
print("Password cannot be empty.")
|
||||
return
|
||||
|
||||
# Hash the new password
|
||||
hashed_password = stauth.Hasher([new_password]).generate()[0]
|
||||
|
||||
# Update the password
|
||||
config['credentials']['usernames'][selected_username]['password'] = hashed_password
|
||||
|
||||
# Save the updated config
|
||||
with open(config_path, 'w') as file:
|
||||
yaml.dump(config, file, default_flow_style=False)
|
||||
|
||||
print(f"Password updated successfully for {selected_username}.")
|
||||
|
||||
except (ValueError, IndexError):
|
||||
print("Invalid input.")
|
||||
except Exception as e:
|
||||
print(f"An error occurred: {e}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Loading…
x
Reference in New Issue
Block a user