added
This commit is contained in:
+214
@@ -0,0 +1,214 @@
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
*.py[codz]
|
||||
*$py.class
|
||||
|
||||
# C extensions
|
||||
*.so
|
||||
|
||||
# Distribution / packaging
|
||||
.Python
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
wheels/
|
||||
share/python-wheels/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
MANIFEST
|
||||
|
||||
# PyInstaller
|
||||
# Usually these files are written by a python script from a template
|
||||
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||
*.manifest
|
||||
*.spec
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
pip-delete-this-directory.txt
|
||||
|
||||
# Unit test / coverage reports
|
||||
htmlcov/
|
||||
.tox/
|
||||
.nox/
|
||||
.coverage
|
||||
.coverage.*
|
||||
.cache
|
||||
nosetests.xml
|
||||
coverage.xml
|
||||
*.cover
|
||||
*.py.cover
|
||||
.hypothesis/
|
||||
.pytest_cache/
|
||||
cover/
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
*.pot
|
||||
|
||||
# Django stuff:
|
||||
*.log
|
||||
local_settings.py
|
||||
db.sqlite3
|
||||
db.sqlite3-journal
|
||||
|
||||
# Flask stuff:
|
||||
instance/
|
||||
.webassets-cache
|
||||
|
||||
# Scrapy stuff:
|
||||
.scrapy
|
||||
|
||||
# Sphinx documentation
|
||||
docs/_build/
|
||||
|
||||
# PyBuilder
|
||||
.pybuilder/
|
||||
target/
|
||||
|
||||
# Jupyter Notebook
|
||||
.ipynb_checkpoints
|
||||
|
||||
# IPython
|
||||
profile_default/
|
||||
ipython_config.py
|
||||
|
||||
# pyenv
|
||||
# For a library or package, you might want to ignore these files since the code is
|
||||
# intended to run in multiple environments; otherwise, check them in:
|
||||
# .python-version
|
||||
|
||||
# pipenv
|
||||
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
||||
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
||||
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
||||
# install all needed dependencies.
|
||||
#Pipfile.lock
|
||||
|
||||
# UV
|
||||
# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
|
||||
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
||||
# commonly ignored for libraries.
|
||||
#uv.lock
|
||||
|
||||
# poetry
|
||||
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
||||
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
||||
# commonly ignored for libraries.
|
||||
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
||||
#poetry.lock
|
||||
#poetry.toml
|
||||
|
||||
# pdm
|
||||
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
||||
# pdm recommends including project-wide configuration in pdm.toml, but excluding .pdm-python.
|
||||
# https://pdm-project.org/en/latest/usage/project/#working-with-version-control
|
||||
#pdm.lock
|
||||
#pdm.toml
|
||||
.pdm-python
|
||||
.pdm-build/
|
||||
|
||||
# pixi
|
||||
# Similar to Pipfile.lock, it is generally recommended to include pixi.lock in version control.
|
||||
#pixi.lock
|
||||
# Pixi creates a virtual environment in the .pixi directory, just like venv module creates one
|
||||
# in the .venv directory. It is recommended not to include this directory in version control.
|
||||
.pixi
|
||||
|
||||
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
||||
__pypackages__/
|
||||
|
||||
# Celery stuff
|
||||
celerybeat-schedule
|
||||
celerybeat.pid
|
||||
|
||||
# SageMath parsed files
|
||||
*.sage.py
|
||||
|
||||
# Environments
|
||||
.env
|
||||
.envrc
|
||||
.venv
|
||||
env/
|
||||
venv/
|
||||
ENV/
|
||||
env.bak/
|
||||
venv.bak/
|
||||
|
||||
# Spyder project settings
|
||||
.spyderproject
|
||||
.spyproject
|
||||
|
||||
# Rope project settings
|
||||
.ropeproject
|
||||
|
||||
# mkdocs documentation
|
||||
/site
|
||||
|
||||
# mypy
|
||||
.mypy_cache/
|
||||
.dmypy.json
|
||||
dmypy.json
|
||||
|
||||
# Pyre type checker
|
||||
.pyre/
|
||||
|
||||
# pytype static type analyzer
|
||||
.pytype/
|
||||
|
||||
# Cython debug symbols
|
||||
cython_debug/
|
||||
|
||||
# PyCharm
|
||||
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
||||
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
||||
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
||||
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
||||
#.idea/
|
||||
|
||||
# Abstra
|
||||
# Abstra is an AI-powered process automation framework.
|
||||
# Ignore directories containing user credentials, local state, and settings.
|
||||
# Learn more at https://abstra.io/docs
|
||||
.abstra/
|
||||
|
||||
# Visual Studio Code
|
||||
# Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore
|
||||
# that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore
|
||||
# and can be added to the global gitignore or merged into this file. However, if you prefer,
|
||||
# you could uncomment the following to ignore the entire vscode folder
|
||||
# .vscode/
|
||||
|
||||
# Ruff stuff:
|
||||
.ruff_cache/
|
||||
|
||||
# PyPI configuration file
|
||||
.pypirc
|
||||
|
||||
# Cursor
|
||||
# Cursor is an AI-powered code editor. `.cursorignore` specifies files/directories to
|
||||
# exclude from AI features like autocomplete and code analysis. Recommended for sensitive data
|
||||
# refer to https://docs.cursor.com/context/ignore-files
|
||||
.cursorignore
|
||||
.cursorindexingignore
|
||||
|
||||
# Marimo
|
||||
marimo/_static/
|
||||
marimo/_lsp/
|
||||
__marimo__/
|
||||
|
||||
# Streamlit
|
||||
.streamlit/secrets.toml
|
||||
|
||||
instance/
|
||||
src/
|
||||
instance/app_data.db
|
||||
+228
@@ -0,0 +1,228 @@
|
||||
|
||||
import os
|
||||
from flask import Flask, request, jsonify, session, send_from_directory
|
||||
from flask_sqlalchemy import SQLAlchemy
|
||||
import os
|
||||
import requests
|
||||
|
||||
app = Flask(__name__, static_folder='../frontend/build', static_url_path='/')
|
||||
app.config['SECRET_KEY'] = 'your_secret_key'
|
||||
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///manga.db'
|
||||
db = SQLAlchemy(app)
|
||||
|
||||
|
||||
|
||||
# --- Database Models ---
|
||||
|
||||
class User(db.Model):
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
username = db.Column(db.String(80), unique=True, nullable=False)
|
||||
password = db.Column(db.String(120), nullable=False)
|
||||
|
||||
class Manga(db.Model):
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
title = db.Column(db.String(120), nullable=False)
|
||||
api_id = db.Column(db.String(120), unique=True, nullable=False)
|
||||
image_url = db.Column(db.String(255), nullable=True)
|
||||
|
||||
class UserManga(db.Model):
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
|
||||
manga_id = db.Column(db.Integer, db.ForeignKey('manga.id'), nullable=False)
|
||||
volume_number = db.Column(db.Integer, nullable=True) # New field for volume number
|
||||
|
||||
class Friend(db.Model):
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
|
||||
friend_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
|
||||
|
||||
# --- API Routes ---
|
||||
|
||||
@app.route('/api/signup', methods=['POST'])
|
||||
def signup():
|
||||
data = request.get_json()
|
||||
username = data.get('username')
|
||||
password = data.get('password')
|
||||
|
||||
if not username or not password:
|
||||
return jsonify({'message': 'Username and password are required'}), 400
|
||||
|
||||
if User.query.filter_by(username=username).first():
|
||||
return jsonify({'message': 'Username already exists'}), 409
|
||||
|
||||
new_user = User(username=username, password=password) # In a real app, hash passwords!
|
||||
db.session.add(new_user)
|
||||
db.session.commit()
|
||||
return jsonify({'message': 'User created successfully'}), 201
|
||||
|
||||
@app.route('/api/login', methods=['POST'])
|
||||
def login():
|
||||
data = request.get_json()
|
||||
username = data.get('username')
|
||||
password = data.get('password')
|
||||
|
||||
user = User.query.filter_by(username=username, password=password).first() # In a real app, check hashed password
|
||||
if user:
|
||||
session['user_id'] = user.id
|
||||
return jsonify({'message': 'Login successful', 'user_id': user.id}), 200
|
||||
return jsonify({'message': 'Invalid credentials'}), 401
|
||||
|
||||
@app.route('/api/logout')
|
||||
def logout():
|
||||
session.pop('user_id', None)
|
||||
return jsonify({'message': 'Logged out successfully'}), 200
|
||||
|
||||
@app.route('/api/dashboard')
|
||||
def dashboard():
|
||||
if 'user_id' not in session:
|
||||
return jsonify({'message': 'Unauthorized'}), 401
|
||||
|
||||
user = User.query.get(session['user_id'])
|
||||
if not user:
|
||||
return jsonify({'message': 'User not found'}), 404
|
||||
|
||||
user_mangas = UserManga.query.filter_by(user_id=user.id).all()
|
||||
grouped_user_mangas = {}
|
||||
for um in user_mangas:
|
||||
manga = Manga.query.get(um.manga_id)
|
||||
if manga:
|
||||
if manga.title not in grouped_user_mangas:
|
||||
grouped_user_mangas[manga.title] = {
|
||||
'manga_details': {
|
||||
'id': manga.id,
|
||||
'title': manga.title,
|
||||
'api_id': manga.api_id,
|
||||
'image_url': manga.image_url
|
||||
},
|
||||
'volumes': []
|
||||
}
|
||||
grouped_user_mangas[manga.title]['volumes'].append(um.volume_number)
|
||||
grouped_user_mangas[manga.title]['volumes'].sort()
|
||||
|
||||
friends = Friend.query.filter_by(user_id=user.id).all()
|
||||
friend_users = []
|
||||
for f in friends:
|
||||
friend_user = User.query.get(f.friend_id)
|
||||
if friend_user:
|
||||
friend_users.append({'id': friend_user.id, 'username': friend_user.username})
|
||||
|
||||
friend_mangas_data = {}
|
||||
for friend_user_data in friend_users:
|
||||
friend_id = friend_user_data['id']
|
||||
friend_username = friend_user_data['username']
|
||||
friend_user_mangas = UserManga.query.filter_by(user_id=friend_id).all()
|
||||
grouped_friend_mangas = {}
|
||||
for fum in friend_user_mangas:
|
||||
fmanga = Manga.query.get(fum.manga_id)
|
||||
if fmanga:
|
||||
if fmanga.title not in grouped_friend_mangas:
|
||||
grouped_friend_mangas[fmanga.title] = {
|
||||
'manga_details': {
|
||||
'id': fmanga.id,
|
||||
'title': fmanga.title,
|
||||
'api_id': fmanga.api_id,
|
||||
'image_url': fmanga.image_url
|
||||
},
|
||||
'volumes': []
|
||||
}
|
||||
grouped_friend_mangas[fmanga.title]['volumes'].append(fum.volume_number)
|
||||
grouped_friend_mangas[fmanga.title]['volumes'].sort()
|
||||
friend_mangas_data[friend_username] = grouped_friend_mangas
|
||||
|
||||
return jsonify({
|
||||
'user': {'id': user.id, 'username': user.username},
|
||||
'mangas': grouped_user_mangas,
|
||||
'friends': friend_users,
|
||||
'friend_mangas': friend_mangas_data
|
||||
}), 200
|
||||
|
||||
@app.route('/api/search', methods=['POST'])
|
||||
def search():
|
||||
if 'user_id' not in session:
|
||||
return jsonify({'message': 'Unauthorized'}), 401
|
||||
|
||||
data = request.get_json()
|
||||
manga_title = data.get('query')
|
||||
if not manga_title:
|
||||
return jsonify({'message': 'Query parameter is required'}), 400
|
||||
|
||||
response = requests.get(f'https://api.jikan.moe/v4/manga?q={manga_title}')
|
||||
manga_data = response.json()
|
||||
return jsonify(manga_data), 200
|
||||
|
||||
@app.route('/api/add_manga', methods=['POST'])
|
||||
def add_manga():
|
||||
if 'user_id' not in session:
|
||||
return jsonify({'message': 'Unauthorized'}), 401
|
||||
|
||||
data = request.get_json()
|
||||
manga_title = data.get('manga_title')
|
||||
manga_api_id = data.get('manga_api_id')
|
||||
image_url = data.get('image_url')
|
||||
volume_number = data.get('volume_number')
|
||||
|
||||
if not manga_title or not manga_api_id:
|
||||
return jsonify({'message': 'Manga title and API ID are required'}), 400
|
||||
|
||||
manga = Manga.query.filter_by(api_id=manga_api_id).first()
|
||||
if not manga:
|
||||
manga = Manga(title=manga_title, api_id=manga_api_id, image_url=image_url)
|
||||
db.session.add(manga)
|
||||
db.session.commit()
|
||||
|
||||
user_manga = UserManga.query.filter_by(
|
||||
user_id=session['user_id'],
|
||||
manga_id=manga.id,
|
||||
volume_number=volume_number
|
||||
).first()
|
||||
|
||||
if not user_manga:
|
||||
user_manga = UserManga(user_id=session['user_id'], manga_id=manga.id, volume_number=volume_number)
|
||||
db.session.add(user_manga)
|
||||
db.session.commit()
|
||||
return jsonify({'message': 'Manga added successfully'}), 201
|
||||
|
||||
@app.route('/api/search_user', methods=['POST'])
|
||||
def search_user():
|
||||
if 'user_id' not in session:
|
||||
return jsonify({'message': 'Unauthorized'}), 401
|
||||
|
||||
data = request.get_json()
|
||||
username = data.get('query')
|
||||
if not username:
|
||||
return jsonify({'message': 'Query parameter is required'}), 400
|
||||
|
||||
users = User.query.filter(User.username.like(f'%{username}%')).all()
|
||||
users_data = [{'id': user.id, 'username': user.username} for user in users]
|
||||
return jsonify({'users': users_data}), 200
|
||||
|
||||
@app.route('/api/add_friend', methods=['POST'])
|
||||
def add_friend():
|
||||
if 'user_id' not in session:
|
||||
return jsonify({'message': 'Unauthorized'}), 401
|
||||
|
||||
data = request.get_json()
|
||||
friend_id = data.get('friend_id')
|
||||
|
||||
if not friend_id:
|
||||
return jsonify({'message': 'Friend ID is required'}), 400
|
||||
|
||||
friendship = Friend.query.filter_by(user_id=session['user_id'], friend_id=friend_id).first()
|
||||
if not friendship:
|
||||
new_friend = Friend(user_id=session['user_id'], friend_id=friend_id)
|
||||
db.session.add(new_friend)
|
||||
db.session.commit()
|
||||
return jsonify({'message': 'Friend added successfully'}), 201
|
||||
|
||||
@app.route('/')
|
||||
def serve_index():
|
||||
return send_from_directory(app.static_folder, 'index.html')
|
||||
|
||||
@app.errorhandler(404)
|
||||
def not_found(e):
|
||||
return send_from_directory(app.static_folder, 'index.html')
|
||||
|
||||
if __name__ == '__main__':
|
||||
with app.app_context():
|
||||
db.create_all()
|
||||
app.run(debug=True)
|
||||
@@ -0,0 +1,22 @@
|
||||
|
||||
body {
|
||||
font-family: sans-serif;
|
||||
margin: 20px;
|
||||
}
|
||||
|
||||
.manga-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.manga-card {
|
||||
border: 1px solid #ccc;
|
||||
padding: 10px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.manga-card img {
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
/.pnp
|
||||
.pnp.js
|
||||
|
||||
# testing
|
||||
/coverage
|
||||
|
||||
# production
|
||||
/build
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
.env.local
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
Generated
+17867
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,42 @@
|
||||
{
|
||||
"name": "frontend",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@testing-library/dom": "^10.4.0",
|
||||
"@testing-library/jest-dom": "^6.6.3",
|
||||
"@testing-library/react": "^16.3.0",
|
||||
"@testing-library/user-event": "^13.5.0",
|
||||
"bootstrap": "^5.3.7",
|
||||
"react": "^19.1.0",
|
||||
"react-bootstrap": "^2.10.10",
|
||||
"react-dom": "^19.1.0",
|
||||
"react-router-dom": "^7.6.3",
|
||||
"react-scripts": "5.0.1",
|
||||
"web-vitals": "^2.1.4"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "react-scripts start",
|
||||
"build": "react-scripts build",
|
||||
"test": "react-scripts test",
|
||||
"eject": "react-scripts eject"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"extends": [
|
||||
"react-app",
|
||||
"react-app/jest"
|
||||
]
|
||||
},
|
||||
"browserslist": {
|
||||
"production": [
|
||||
">0.2%",
|
||||
"not dead",
|
||||
"not op_mini all"
|
||||
],
|
||||
"development": [
|
||||
"last 1 chrome version",
|
||||
"last 1 firefox version",
|
||||
"last 1 safari version"
|
||||
]
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 3.8 KiB |
@@ -0,0 +1,43 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="theme-color" content="#000000" />
|
||||
<meta
|
||||
name="description"
|
||||
content="Web site created using create-react-app"
|
||||
/>
|
||||
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
|
||||
<!--
|
||||
manifest.json provides metadata used when your web app is installed on a
|
||||
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
|
||||
-->
|
||||
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
|
||||
<!--
|
||||
Notice the use of %PUBLIC_URL% in the tags above.
|
||||
It will be replaced with the URL of the `public` folder during the build.
|
||||
Only files inside the `public` folder can be referenced from the HTML.
|
||||
|
||||
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
|
||||
work correctly both with client-side routing and a non-root public URL.
|
||||
Learn how to configure a non-root public URL by running `npm run build`.
|
||||
-->
|
||||
<title>React App</title>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
<div id="root"></div>
|
||||
<!--
|
||||
This HTML file is a template.
|
||||
If you open it directly in the browser, you will see an empty page.
|
||||
|
||||
You can add webfonts, meta tags, or analytics to this file.
|
||||
The build step will place the bundled scripts into the <body> tag.
|
||||
|
||||
To begin the development, run `npm start` or `yarn start`.
|
||||
To create a production bundle, use `npm run build` or `yarn build`.
|
||||
-->
|
||||
</body>
|
||||
</html>
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 5.2 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 9.4 KiB |
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"short_name": "React App",
|
||||
"name": "Create React App Sample",
|
||||
"icons": [
|
||||
{
|
||||
"src": "favicon.ico",
|
||||
"sizes": "64x64 32x32 24x24 16x16",
|
||||
"type": "image/x-icon"
|
||||
},
|
||||
{
|
||||
"src": "logo192.png",
|
||||
"type": "image/png",
|
||||
"sizes": "192x192"
|
||||
},
|
||||
{
|
||||
"src": "logo512.png",
|
||||
"type": "image/png",
|
||||
"sizes": "512x512"
|
||||
}
|
||||
],
|
||||
"start_url": ".",
|
||||
"display": "standalone",
|
||||
"theme_color": "#000000",
|
||||
"background_color": "#ffffff"
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
# https://www.robotstxt.org/robotstxt.html
|
||||
User-agent: *
|
||||
Disallow:
|
||||
@@ -0,0 +1,30 @@
|
||||
altgraph @ file:///AppleInternal/Library/BuildRoots/39d9dc1a-2111-11f0-be06-226177e5bb69/Library/Caches/com.apple.xbs/Sources/python3/altgraph-0.17.2-py2.py3-none-any.whl
|
||||
blinker==1.7.0
|
||||
certifi==2025.7.9
|
||||
charset-normalizer==3.4.2
|
||||
click==8.1.7
|
||||
Flask==3.0.2
|
||||
Flask-SQLAlchemy==3.1.1
|
||||
future @ file:///AppleInternal/Library/BuildRoots/39d9dc1a-2111-11f0-be06-226177e5bb69/Library/Caches/com.apple.xbs/Sources/python3/future-0.18.2-py3-none-any.whl
|
||||
greenlet==3.0.3
|
||||
gunicorn==21.2.0
|
||||
idna==3.10
|
||||
importlib_metadata==8.7.0
|
||||
itsdangerous==2.1.2
|
||||
Jinja2==3.1.3
|
||||
macholib @ file:///AppleInternal/Library/BuildRoots/39d9dc1a-2111-11f0-be06-226177e5bb69/Library/Caches/com.apple.xbs/Sources/python3/macholib-1.15.2-py2.py3-none-any.whl
|
||||
MarkupSafe==2.1.5
|
||||
numpy==1.26.4
|
||||
packaging==23.2
|
||||
pandas==2.2.1
|
||||
python-dateutil==2.9.0.post0
|
||||
pytz==2024.1
|
||||
requests==2.32.4
|
||||
schedule==1.2.2
|
||||
six==1.16.0
|
||||
SQLAlchemy==2.0.28
|
||||
typing_extensions==4.10.0
|
||||
tzdata==2024.1
|
||||
urllib3==2.5.0
|
||||
Werkzeug==3.0.1
|
||||
zipp==3.23.0
|
||||
Reference in New Issue
Block a user