diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml index a4183fa4a..87ea6f167 100644 --- a/.github/workflows/ci-cd.yml +++ b/.github/workflows/ci-cd.yml @@ -53,6 +53,9 @@ jobs: restore-keys: | ${{ runner.os }}-poetry- + - name: Print memory usage + run: free -h + pre-commit: name: Run pre-commit needs: setup @@ -69,6 +72,8 @@ jobs: python-version: 3.11.2 - name: Run pre-commit uses: pre-commit/action@v3.0.1 + - name: Print memory usage + run: free -h code-ql: name: Run CodeQL @@ -132,6 +137,9 @@ jobs: - uses: github/codeql-action/analyze@v2 if: ${{ steps.set-matrix.outputs.languages != '' }} + - name: Print memory usage + run: free -h + test: name: Run Tests needs: code-ql @@ -147,11 +155,13 @@ jobs: with: python-version: 3.11.2 - run: pip install poetry - - run: poetry lock --no-update + - run: poetry lock - run: poetry install - run: poetry run python manage.py collectstatic --noinput - name: Run tests run: poetry run xvfb-run --auto-servernum python manage.py test -v 3 --failfast + - name: Print memory usage + run: free -h docker-test: runs-on: ubuntu-latest @@ -176,10 +186,13 @@ jobs: docker run -d --name my-container my-app - run: docker exec my-container pip install poetry - - run: docker exec my-container poetry lock --no-update - - run: docker exec my-container poetry install --no-dev --no-interaction + - run: docker exec my-container poetry lock + - run: docker exec my-container poetry install --without dev --no-interaction - name: Clean up run: | docker stop my-container - docker rm my-container \ No newline at end of file + docker rm my-container + + - name: Print memory usage + run: free -h \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 048b84014..80f86acfc 100644 --- a/Dockerfile +++ b/Dockerfile @@ -33,8 +33,8 @@ RUN ln -s /usr/bin/google-chrome-stable /usr/local/bin/google-chrome RUN pip install poetry RUN poetry config virtualenvs.create false COPY pyproject.toml poetry.lock* ./ -RUN poetry lock --no-update -RUN poetry install +RUN poetry lock +RUN poetry install --no-root # Install additional Python packages RUN pip install opentelemetry-api opentelemetry-instrumentation diff --git a/Procfile b/Procfile index 42fce1a42..0945ffcee 100644 --- a/Procfile +++ b/Procfile @@ -1,2 +1,3 @@ release: python manage.py migrate --noinput -web: bin/start-pgbouncer uvicorn blt.asgi:application --host 0.0.0.0 --port ${PORT} +#web: newrelic-admin run-program bin/start-pgbouncer uvicorn blt.asgi:application --host 0.0.0.0 --port ${PORT} +web: gunicorn blt.wsgi --log-file - --workers 1 --worker-class gthread --threads 2 --timeout 120 \ No newline at end of file diff --git a/blt/asgi.py b/blt/asgi.py index 4efa5e4b1..ffee68e08 100644 --- a/blt/asgi.py +++ b/blt/asgi.py @@ -1,6 +1,7 @@ # asgi.py import os +import tracemalloc import django @@ -12,6 +13,8 @@ from django.core.asgi import get_asgi_application from django.urls import path +tracemalloc.start() + from website import consumers # You will define a consumer for handling WebSockets application = ProtocolTypeRouter( diff --git a/blt/settings.py b/blt/settings.py index dfeb113f8..ae9f8bf1e 100644 --- a/blt/settings.py +++ b/blt/settings.py @@ -1,13 +1,3 @@ -""" -Django settings for gettingstarted project, on Heroku. For more info, see: -https://github.com/heroku/heroku-django-template -For more information on this file, see -https://docs.djangoproject.com/en/1.8/topics/settings/ -For the full list of settings and their values, see -https://docs.djangoproject.com/en/1.8/ref/settings/ -""" - -# from google.oauth2 import service_account import json import os import sys @@ -17,15 +7,16 @@ from django.utils.translation import gettext_lazy as _ from google.oauth2 import service_account -# reading .env file environ.Env.read_env() -# Build paths inside the project like this: os.path.join(BASE_DIR, ...) BASE_DIR = os.path.dirname(os.path.dirname(__file__)) env = environ.Env() env_file = os.path.join(BASE_DIR, ".env") environ.Env.read_env(env_file) +print(f"Reading .env file from {env_file}") +print(f"DATABASE_URL: {os.environ.get('DATABASE_URL', 'not set')}") + PROJECT_ROOT = os.path.dirname(os.path.abspath(__file__)) GITHUB_TOKEN = os.environ.get("GITHUB_TOKEN", "blank") @@ -34,44 +25,40 @@ DOMAIN_NAME = "blt.owasp.org" FQDN = "blt.owasp.org" DOMAIN_NAME_PREVIOUS = os.environ.get("DOMAIN_NAME_PREVIOUS", "BLT") -# else: -# # Default values if hostname does not match -# PROJECT_NAME = os.environ.get("PROJECT_NAME", "BLT") -# DOMAIN_NAME = os.environ.get("DOMAIN_NAME", "127.0.0.1") -# FQDN = "www." + DOMAIN_NAME PROJECT_NAME_LOWER = PROJECT_NAME.lower() PROJECT_NAME_UPPER = PROJECT_NAME.upper() ADMIN_URL = os.environ.get("ADMIN_URL", "admin") PORT = os.environ.get("PORT", "8000") -DEFAULT_FROM_EMAIL = os.environ.get("FROM_EMAIL", "test@localhost") -SERVER_EMAIL = os.environ.get("FROM_EMAIL", "test@localhost") +DEFAULT_FROM_EMAIL = os.environ.get("FROM_EMAIL", "blt-support@owasp.org") +SERVER_EMAIL = os.environ.get("FROM_EMAIL", "blt-support@owasp.org") EMAIL_TO_STRING = PROJECT_NAME + " <" + SERVER_EMAIL + ">" -BLOG_URL = os.environ.get("BLOG_URL", "https://owasp.org/www-project-bug-logging-tool/") +BLOG_URL = os.environ.get("BLOG_URL", FQDN + "/blog/") FACEBOOK_URL = os.environ.get("FACEBOOK_URL", "https://www.facebook.com/groups/owaspfoundation/") -TWITTER_URL = os.environ.get("TWITTER_URL", "https://twitter.com/owasp") +TWITTER_URL = os.environ.get("TWITTER_URL", "https://twitter.com/owasp_blt") GITHUB_URL = os.environ.get("GITHUB_URL", "https://github.com/OWASP/BLT") -EXTENSION_URL = os.environ.get("EXTENSION_URL", "https://github.com/OWASP/BLT") +EXTENSION_URL = os.environ.get("EXTENSION_URL", "https://github.com/OWASP/BLT-Extension") ADMINS = (("Admin", DEFAULT_FROM_EMAIL),) -# Quick-start development settings - unsuitable for production -# See https://docs.djangoproject.com/en/1.8/howto/deployment/checklist/ - -# SECURITY WARNING: change this before deploying to production! SECRET_KEY = "i+acxn5(akgsn!sr4^qgf(^m&*@+g1@u^t@=8s@axc41ml*f=s" -# SECURITY WARNING: don't run with debug turned on in production! DEBUG = False TESTING = sys.argv[1:2] == ["test"] SITE_ID = 1 -# Application definition + +# Scout settings +SCOUT_MONITOR = True +SCOUT_KEY = os.environ.get("SCOUT_KEY") +SCOUT_NAME = PROJECT_NAME + INSTALLED_APPS = ( + # "scout_apm.django", "django.contrib.admin", "django.contrib.auth", "django.contrib.contenttypes", @@ -95,11 +82,8 @@ "rest_framework", "django_filters", "rest_framework.authtoken", - # "django_cron", "mdeditor", - # "bootstrap_datepicker_plus", "tz_detect", - # "tellme", "star_ratings", "drf_yasg", "captcha", @@ -187,34 +171,12 @@ "allauth.account.auth_backends.AuthenticationBackend", ) -# SESSION_ENGINE = 'django.contrib.sessions.backends.cached_db' - -# CACHES = { -# 'default': { -# 'BACKEND': 'django.core.cache.backends.db.DatabaseCache', -# 'LOCATION': 'cache_table', -# } -# } - REST_AUTH = {"SESSION_LOGIN": False} CONN_MAX_AGE = None WSGI_APPLICATION = "blt.wsgi.application" -# Database -# https://docs.djangoproject.com/en/1.9/ref/settings/#databases - -DATABASES = { - "default": { - "ENGINE": "django.db.backends.sqlite3", - "NAME": os.path.join(BASE_DIR, "db.sqlite3"), - } -} - -# Password validation -# https://docs.djangoproject.com/en/1.9/ref/settings/#auth-password-validators - AUTH_PASSWORD_VALIDATORS = [ { "NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator", @@ -230,8 +192,6 @@ }, ] -# Internationalization -# https://docs.djangoproject.com/en/1.8/topics/i18n/ LANGUAGE_CODE = "en-us" TIME_ZONE = "UTC" @@ -253,22 +213,17 @@ MEDIA_ROOT = "media" MEDIA_URL = "/media/" -# Update database configuration with $DATABASE_URL. db_from_env = dj_database_url.config(conn_max_age=500) -DATABASES["default"].update(db_from_env) + EMAIL_HOST = "localhost" EMAIL_PORT = 1025 -# python -m smtpd -n -c DebuggingServer localhost:1025 -# if DEBUG: -# EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend' + REPORT_EMAIL = os.environ.get("REPORT_EMAIL", "blank") REPORT_EMAIL_PASSWORD = os.environ.get("REPORT_PASSWORD", "blank") -# these settings are only for production / Heroku -if "DYNO" in os.environ: - print("database url detected in settings") +if "DYNO" in os.environ: # for Heroku DEBUG = False EMAIL_HOST = "smtp.sendgrid.net" EMAIL_HOST_USER = os.environ.get("SENDGRID_USERNAME", "blank") @@ -278,26 +233,11 @@ if not TESTING: SECURE_SSL_REDIRECT = True - import logging + # import logging - logging.basicConfig(level=logging.DEBUG) - # GS_ACCESS_KEY_ID = os.environ.get("GS_ACCESS_KEY_ID", "blank") - # GS_SECRET_ACCESS_KEY = os.environ.get("GS_SECRET_ACCESS_KEY", "blank") - # GOOGLE_APPLICATION_CREDENTIALS = "/app/google-credentials.json" + # logging.basicConfig(level=logging.DEBUG) GS_BUCKET_NAME = "bhfiles" - # DEFAULT_FILE_STORAGE = "storages.backends.gcloud.GoogleCloudStorage" - - # GS_CREDENTIALS = None - - # # Ensure credentials file is valid - # try: - # GS_CREDENTIALS = service_account.Credentials.from_service_account_file( - # GOOGLE_APPLICATION_CREDENTIALS - # ) - # print("Google Cloud Storage credentials loaded successfully.") - # except Exception as e: - # print(f"Error loading Google Cloud Storage credentials: {e}") GOOGLE_CREDENTIALS = os.getenv("GOOGLE_CREDENTIALS") @@ -325,7 +265,6 @@ GS_QUERYSTRING_AUTH = False GS_DEFAULT_ACL = None MEDIA_URL = "https://bhfiles.storage.googleapis.com/" - # add debugging info for google storage import sentry_sdk from sentry_sdk.integrations.django import DjangoIntegration @@ -349,24 +288,34 @@ }, } DEFAULT_FILE_STORAGE = "storages.backends.gcloud.GoogleCloudStorage" - # DEFAULT_FILE_STORAGE = "django.core.files.storage.FileSystemStorage" - print("no database url detected in settings, using sqlite") if not TESTING: DEBUG = True -# local dev needs to set SMTP backend or fail at startup -if DEBUG: - EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend" + # use this to debug emails locally + # python -m smtpd -n -c DebuggingServer localhost:1025 + if DEBUG: + EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend" + +DATABASES = { + "default": { + "ENGINE": "django.db.backends.sqlite3", + "NAME": os.path.join(BASE_DIR, "db.sqlite3"), + } +} + +if not db_from_env: + print("no database url detected in settings, using sqlite") +else: + print("using database url: ", db_from_env) + DATABASES["default"].update(db_from_env) + ACCOUNT_EMAIL_REQUIRED = True ACCOUNT_USERNAME_REQUIRED = True ACCOUNT_EMAIL_VERIFICATION = "optional" -# Honor the 'X-Forwarded-Proto' header for request.is_secure() - SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https") -# Allow all host headers ALLOWED_HOSTS = [ "." + DOMAIN_NAME, "127.0.0.1", @@ -376,23 +325,16 @@ "." + DOMAIN_NAME_PREVIOUS, ] -# Static files (CSS, JavaScript, Images) -# https://docs.djangoproject.com/en/1.8/howto/static-files/ STATIC_ROOT = os.path.join(PROJECT_ROOT, "staticfiles") STATIC_URL = "/static/" -# Extra places for collectstatic to find static files. STATICFILES_DIRS = (os.path.join(BASE_DIR, "website", "static"),) ABSOLUTE_URL_OVERRIDES = { "auth.user": lambda u: "/profile/%s/" % u.username, } -# Simplified static file serving. -# https://warehouse.python.org/project/whitenoise/ -# STATICFILES_STORAGE = "whitenoise.storage.CompressedStaticFilesStorage" - LOGIN_REDIRECT_URL = "/" LOGGING = { @@ -413,6 +355,16 @@ }, }, } +# disable logging unless critical + +# LOGGING = { +# "version": 1, +# "disable_existing_loggers": True, # Disable all existing loggers +# "handlers": {}, # No handlers defined +# "loggers": {}, # No loggers defined +# } + + USERS_AVATAR_PATH = "avatars" AVATAR_PATH = os.path.join(MEDIA_ROOT, USERS_AVATAR_PATH) @@ -457,16 +409,24 @@ } } else: + # temp to check memory usage CACHES = { "default": { - "BACKEND": "django_redis.cache.RedisCache", - "LOCATION": os.environ.get("REDISCLOUD_URL"), - "OPTIONS": { - "CLIENT_CLASS": "django_redis.client.DefaultClient", - }, + "BACKEND": "django.core.cache.backends.locmem.LocMemCache", + "LOCATION": "unique-snowflake", } } + # CACHES = { + # "default": { + # "BACKEND": "django_redis.cache.RedisCache", + # "LOCATION": os.environ.get("REDISCLOUD_URL"), + # "OPTIONS": { + # "CLIENT_CLASS": "django_redis.client.DefaultClient", + # }, + # } + # } + if DEBUG or TESTING: anon_throttle = 100000 user_throttle = 100000 @@ -579,37 +539,22 @@ "STRIPE_TEST_SECRET_KEY", "sk_test_12345", ) -STRIPE_LIVE_MODE = False # Change to True in production +STRIPE_LIVE_MODE = False # TODO: remove stripe DEFAULT_AUTO_FIELD = "django.db.models.AutoField" -# CALLBACK_URL_FOR_GITHUB = os.environ.get( -# "CALLBACK_URL_FOR_GITHUB", default="https://www." + DOMAIN_NAME +"/") - -# CALLBACK_URL_FOR_GOOGLE = os.environ.get( -# "CALLBACK_URL_FOR_GOOGLE", default="https://www." + DOMAIN_NAME +"/") - -# CALLBACK_URL_FOR_FACEBOOK = os.environ.get( -# "CALLBACK_URL_FOR_FACEBOOK", default="https://www." + DOMAIN_NAME +"/") - - -# allow captcha bypass during test IS_TEST = False if "test" in sys.argv: CAPTCHA_TEST_MODE = True IS_TEST = True - -# Twitter - +# Twitter API - we can remove these - update names to have twitter_x or bluesky_x BEARER_TOKEN = os.environ.get("BEARER_TOKEN") APP_KEY = os.environ.get("APP_KEY") APP_KEY_SECRET = os.environ.get("APP_KEY_SECRET") ACCESS_TOKEN = os.environ.get("ACCESS_TOKEN") ACCESS_TOKEN_SECRET = os.environ.get("ACCESS_TOKEN_SECRET") -# USPTO - USPTO_API = os.environ.get("USPTO_API") @@ -625,6 +570,7 @@ "BACKEND": "channels_redis.core.RedisChannelLayer", "CONFIG": { "hosts": [os.environ.get("REDISCLOUD_URL")], + # "hosts": [("127.0.0.1", 6379)], }, }, } diff --git a/blt/urls.py b/blt/urls.py index 21c538136..56bbb7fc8 100644 --- a/blt/urls.py +++ b/blt/urls.py @@ -63,7 +63,7 @@ delete_prize, edit_prize, ) -from website.views.core import ( +from website.views.core import ( # chatbot_conversation, FacebookConnect, FacebookLogin, GithubConnect, @@ -74,7 +74,6 @@ UploadCreate, add_suggestions, badge_list, - chatbot_conversation, check_status, donate_view, facebook_callback, @@ -101,9 +100,9 @@ SpecificIssuesView, UpdateIssue, change_bid_status, - comment_on_issue, + comment_on_content, create_github_issue, - delete_comment, + delete_content_comment, delete_issue, dislike_issue, fetch_current_bid, @@ -121,7 +120,7 @@ submit_bug, submit_pr, unsave_issue, - update_comment, + update_content_comment, vote_count, ) from website.views.organization import ( @@ -175,12 +174,14 @@ view_hunt, weekly_report, ) -from website.views.project import ( - ProjectBadgeView, +from website.views.project import ( # ProjectBadgeView, ProjectDetailView, ProjectListView, + ProjectsDetailView, ProjectView, + RepoDetailView, blt_tomato, + create_project, distribute_bacon, select_contribution, ) @@ -294,7 +295,9 @@ path("auth/facebook/url/", facebook_views.oauth2_callback), path("socialaccounts/", SocialAccountListView.as_view(), name="social_account_list"), path( - "add_domain_to_organization/", add_domain_to_organization, name="add_domain_to_organization" + "add_domain_to_organization/", + add_domain_to_organization, + name="add_domain_to_organization", ), path( "socialaccounts//disconnect/", @@ -375,9 +378,17 @@ admin_organization_dashboard_detail, name="admin_organization_dashboard_detail", ), - re_path(r"^dashboard/organization/hunt/create$", CreateHunt.as_view(), name="create_hunt"), + re_path( + r"^dashboard/organization/hunt/create$", + CreateHunt.as_view(), + name="create_hunt", + ), path("hunt/", ShowBughuntView.as_view(), name="show_bughunt"), - re_path(r"^dashboard/organization/hunt/drafts$", DraftHunts.as_view(), name="draft_hunts"), + re_path( + r"^dashboard/organization/hunt/drafts$", + DraftHunts.as_view(), + name="draft_hunts", + ), re_path( r"^dashboard/organization/hunt/upcoming$", UpcomingHunts.as_view(), @@ -430,7 +441,11 @@ ), re_path(r"^flag_issue/(?P\d+)/$", flag_issue, name="flag_issue"), re_path(r"^resolve/(?P\w+)/$", resolve, name="resolve"), - re_path(r"^create_github_issue/(?P\w+)/$", create_github_issue, name="create_github_issue"), + re_path( + r"^create_github_issue/(?P\w+)/$", + create_github_issue, + name="create_github_issue", + ), re_path(r"^vote_count/(?P\d+)/$", vote_count, name="vote_count"), path("domain//subscribe/", subscribe_to_domains, name="subscribe_to_domains"), re_path(r"^save_issue/(?P\d+)/$", save_issue, name="save_issue"), @@ -446,19 +461,20 @@ ), re_path(r"^issue/edit/$", IssueEdit, name="edit_issue"), re_path(r"^issue/update/$", UpdateIssue, name="update_issue"), + # comment on content path( - "issue//comment/", - comment_on_issue, - name="comment_on_issue", + "content//comment/", + comment_on_content, + name="comment_on_content", ), - # UPDATE COMMENT + # update comment path( - "issue//comment/update//", - update_comment, - name="update_comment", + "content//comment/update//", + update_content_comment, + name="update_content_comment", ), - # delete_comment - path("issue2/comment/delete/", delete_comment, name="delete_comment"), + # delete comment + path("content/comment/delete/", delete_content_comment, name="delete_content_comment"), re_path(r"^issue/(?P\w+)/$", IssueView.as_view(), name="issue_view"), re_path(r"^follow/(?P[^/]+)/", follow_user, name="follow_user"), re_path(r"^all_activity/$", AllIssuesView.as_view(), name="all_activity"), @@ -591,7 +607,10 @@ re_path(r"^api/v1/count/$", issue_count, name="api_count"), re_path(r"^api/v1/contributors/$", contributors, name="api_contributor"), path("project//", ProjectDetailView.as_view(), name="project_view"), - path("projects//badge/", ProjectBadgeView.as_view(), name="project-badge"), + # path( + # "projects//badge/", ProjectBadgeView.as_view(), name="project-badge" + # ), + path("repository//", RepoDetailView.as_view(), name="repo_detail"), re_path(r"^report-ip/$", ReportIpView.as_view(), name="report_ip"), re_path(r"^reported-ips/$", ReportedIpListView.as_view(), name="reported_ips_list"), re_path(r"^feed/$", feed, name="feed"), @@ -658,7 +677,11 @@ # users path("users/", users_view, name="users"), # company specific urls : - path("organization/", RegisterOrganizationView.as_view(), name="register_organization"), + path( + "organization/", + RegisterOrganizationView.as_view(), + name="register_organization", + ), path("organization/dashboard/", Organization_view, name="organization_view"), path( "organization//dashboard/analytics/", @@ -691,10 +714,20 @@ name="organization_manage_bughunts", ), path( - "organization/dashboard/end_bughunt/", EndBughuntView.as_view(), name="end_bughunt" + "organization/dashboard/end_bughunt/", + EndBughuntView.as_view(), + name="end_bughunt", + ), + path( + "organization//dashboard/add_bughunt/", + AddHuntView.as_view(), + name="add_bughunt", + ), + path( + "organization//dashboard/add_domain/", + AddDomainView.as_view(), + name="add_domain", ), - path("organization//dashboard/add_bughunt/", AddHuntView.as_view(), name="add_bughunt"), - path("organization//dashboard/add_domain/", AddDomainView.as_view(), name="add_domain"), path( "organization//dashboard/add_slack_integration/", AddSlackIntegrationView.as_view(), @@ -705,7 +738,11 @@ AddDomainView.as_view(), name="edit_domain", ), - path("organization/domain//", login_required(DomainView.as_view()), name="view_domain"), + path( + "organization/domain//", + login_required(DomainView.as_view()), + name="view_domain", + ), path( "organization/delete_prize//", delete_prize, @@ -716,7 +753,11 @@ edit_prize, name="edit_prize", ), - path("organization/accept_bug///", accept_bug, name="accept_bug"), + path( + "organization/accept_bug///", + accept_bug, + name="accept_bug", + ), path( "organization/accept_bug///", accept_bug, @@ -731,7 +772,11 @@ path("donate/", donate_view, name="donate"), path("organizations/", DomainListView.as_view(), name="domain_lists"), path("trademarks/", trademark_search, name="trademark_search"), - path("generate_bid_image//", generate_bid_image, name="generate_bid_image"), + path( + "generate_bid_image//", + generate_bid_image, + name="generate_bid_image", + ), path("bidding/", SaveBiddingData, name="BiddingData"), path("select_bid/", select_bid, name="select_bid"), path("get_unique_issues/", get_unique_issues, name="get_unique_issues"), @@ -753,15 +798,25 @@ update_bch_address, name="update_bch_address", ), - path("api/chatbot/conversation/", chatbot_conversation, name="chatbot_conversation"), + # path( + # "api/chatbot/conversation/", chatbot_conversation, name="chatbot_conversation" + # ), path("blt-tomato/", blt_tomato, name="blt-tomato"), path( "api/v1/projects/", ProjectViewSet.as_view({"get": "list", "post": "create", "patch": "update"}), name="projects_api", ), - path("auth/delete", AuthApiViewset.as_view({"delete": "delete"}), name="auth-delete-api"), - path("api/v1/tags", TagApiViewset.as_view({"get": "list", "post": "create"}), name="tags-api"), + path( + "auth/delete", + AuthApiViewset.as_view({"delete": "delete"}), + name="auth-delete-api", + ), + path( + "api/v1/tags", + TagApiViewset.as_view({"get": "list", "post": "create"}), + name="tags-api", + ), path("sizzle/", sizzle, name="sizzle"), path("check-in/", checkIN, name="checkIN"), path("add-sizzle-checkin/", add_sizzle_checkIN, name="add_sizzle_checkin"), @@ -800,6 +855,8 @@ TemplateView.as_view(template_name="similarity.html"), name="similarity_scan", ), + path("projects/create/", create_project, name="create_project"), + path("projects//", ProjectsDetailView.as_view(), name="projects_detail"), path("teams/challenges/", TeamChallenges.as_view(), name="team_challenges"), path("teams/leaderboard/", TeamLeaderboard.as_view(), name="team_leaderboard"), path("challenges/", UserChallengeListView.as_view(), name="user_challenges"), diff --git a/comments/admin.py b/comments/admin.py index cd523ddba..543c2f553 100644 --- a/comments/admin.py +++ b/comments/admin.py @@ -4,7 +4,12 @@ class MyCommentsAdmin(admin.ModelAdmin): - list_display = ("id", "author", "issue", "text", "created_date") + list_display = ("id", "author", "get_related_object", "text", "created_date") + + def get_related_object(self, obj): + return obj.content_object + + get_related_object.short_description = "Related Object" admin.site.register(Comment, MyCommentsAdmin) diff --git a/comments/migrations/0007_remove_comment_issue_comment_content_type_and_more.py b/comments/migrations/0007_remove_comment_issue_comment_content_type_and_more.py new file mode 100644 index 000000000..53abf2f3e --- /dev/null +++ b/comments/migrations/0007_remove_comment_issue_comment_content_type_and_more.py @@ -0,0 +1,36 @@ +# Generated by Django 5.1.4 on 2025-01-04 19:10 + +import django.db.models.deletion +import django.utils.timezone +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("comments", "0006_comment_author_fk"), + ("contenttypes", "0002_remove_content_type_name"), + ] + + operations = [ + migrations.RemoveField( + model_name="comment", + name="issue", + ), + migrations.AddField( + model_name="comment", + name="content_type", + field=models.ForeignKey( + default=1, + on_delete=django.db.models.deletion.CASCADE, + to="contenttypes.contenttype", + ), + preserve_default=False, + ), + migrations.AddField( + model_name="comment", + name="object_id", + field=models.PositiveIntegerField(default=1), + preserve_default=False, + ), + ] diff --git a/comments/models.py b/comments/models.py index 2493fca80..767b51ac1 100644 --- a/comments/models.py +++ b/comments/models.py @@ -1,14 +1,18 @@ +from django.contrib.contenttypes.fields import GenericForeignKey +from django.contrib.contenttypes.models import ContentType from django.db import models from django.utils import timezone -from website.models import Issue, UserProfile +from website.models import UserProfile # Create your models here. class Comment(models.Model): parent = models.ForeignKey("self", null=True, on_delete=models.CASCADE) - issue = models.ForeignKey(Issue, on_delete=models.CASCADE, related_name="comments") + content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE) + object_id = models.PositiveIntegerField() + content_object = GenericForeignKey("content_type", "object_id") author = models.CharField(max_length=200) author_fk = models.ForeignKey(UserProfile, null=True, on_delete=models.SET_NULL) author_url = models.CharField(max_length=200) diff --git a/poetry.lock b/poetry.lock index fff55cf14..716435368 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.8.5 and should not be changed by hand. [[package]] name = "aiohappyeyeballs" @@ -500,116 +500,103 @@ files = [ [[package]] name = "charset-normalizer" -version = "3.4.0" +version = "3.4.1" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." optional = false -python-versions = ">=3.7.0" +python-versions = ">=3.7" files = [ - {file = "charset_normalizer-3.4.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:4f9fc98dad6c2eaa32fc3af1417d95b5e3d08aff968df0cd320066def971f9a6"}, - {file = "charset_normalizer-3.4.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0de7b687289d3c1b3e8660d0741874abe7888100efe14bd0f9fd7141bcbda92b"}, - {file = "charset_normalizer-3.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5ed2e36c3e9b4f21dd9422f6893dec0abf2cca553af509b10cd630f878d3eb99"}, - {file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40d3ff7fc90b98c637bda91c89d51264a3dcf210cade3a2c6f838c7268d7a4ca"}, - {file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1110e22af8ca26b90bd6364fe4c763329b0ebf1ee213ba32b68c73de5752323d"}, - {file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:86f4e8cca779080f66ff4f191a685ced73d2f72d50216f7112185dc02b90b9b7"}, - {file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f683ddc7eedd742e2889d2bfb96d69573fde1d92fcb811979cdb7165bb9c7d3"}, - {file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:27623ba66c183eca01bf9ff833875b459cad267aeeb044477fedac35e19ba907"}, - {file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f606a1881d2663630ea5b8ce2efe2111740df4b687bd78b34a8131baa007f79b"}, - {file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:0b309d1747110feb25d7ed6b01afdec269c647d382c857ef4663bbe6ad95a912"}, - {file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:136815f06a3ae311fae551c3df1f998a1ebd01ddd424aa5603a4336997629e95"}, - {file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:14215b71a762336254351b00ec720a8e85cada43b987da5a042e4ce3e82bd68e"}, - {file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:79983512b108e4a164b9c8d34de3992f76d48cadc9554c9e60b43f308988aabe"}, - {file = "charset_normalizer-3.4.0-cp310-cp310-win32.whl", hash = "sha256:c94057af19bc953643a33581844649a7fdab902624d2eb739738a30e2b3e60fc"}, - {file = "charset_normalizer-3.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:55f56e2ebd4e3bc50442fbc0888c9d8c94e4e06a933804e2af3e89e2f9c1c749"}, - {file = "charset_normalizer-3.4.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:0d99dd8ff461990f12d6e42c7347fd9ab2532fb70e9621ba520f9e8637161d7c"}, - {file = "charset_normalizer-3.4.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c57516e58fd17d03ebe67e181a4e4e2ccab1168f8c2976c6a334d4f819fe5944"}, - {file = "charset_normalizer-3.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6dba5d19c4dfab08e58d5b36304b3f92f3bd5d42c1a3fa37b5ba5cdf6dfcbcee"}, - {file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bf4475b82be41b07cc5e5ff94810e6a01f276e37c2d55571e3fe175e467a1a1c"}, - {file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ce031db0408e487fd2775d745ce30a7cd2923667cf3b69d48d219f1d8f5ddeb6"}, - {file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8ff4e7cdfdb1ab5698e675ca622e72d58a6fa2a8aa58195de0c0061288e6e3ea"}, - {file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3710a9751938947e6327ea9f3ea6332a09bf0ba0c09cae9cb1f250bd1f1549bc"}, - {file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:82357d85de703176b5587dbe6ade8ff67f9f69a41c0733cf2425378b49954de5"}, - {file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:47334db71978b23ebcf3c0f9f5ee98b8d65992b65c9c4f2d34c2eaf5bcaf0594"}, - {file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:8ce7fd6767a1cc5a92a639b391891bf1c268b03ec7e021c7d6d902285259685c"}, - {file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:f1a2f519ae173b5b6a2c9d5fa3116ce16e48b3462c8b96dfdded11055e3d6365"}, - {file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:63bc5c4ae26e4bc6be6469943b8253c0fd4e4186c43ad46e713ea61a0ba49129"}, - {file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:bcb4f8ea87d03bc51ad04add8ceaf9b0f085ac045ab4d74e73bbc2dc033f0236"}, - {file = "charset_normalizer-3.4.0-cp311-cp311-win32.whl", hash = "sha256:9ae4ef0b3f6b41bad6366fb0ea4fc1d7ed051528e113a60fa2a65a9abb5b1d99"}, - {file = "charset_normalizer-3.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:cee4373f4d3ad28f1ab6290684d8e2ebdb9e7a1b74fdc39e4c211995f77bec27"}, - {file = "charset_normalizer-3.4.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0713f3adb9d03d49d365b70b84775d0a0d18e4ab08d12bc46baa6132ba78aaf6"}, - {file = "charset_normalizer-3.4.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:de7376c29d95d6719048c194a9cf1a1b0393fbe8488a22008610b0361d834ecf"}, - {file = "charset_normalizer-3.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4a51b48f42d9358460b78725283f04bddaf44a9358197b889657deba38f329db"}, - {file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b295729485b06c1a0683af02a9e42d2caa9db04a373dc38a6a58cdd1e8abddf1"}, - {file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ee803480535c44e7f5ad00788526da7d85525cfefaf8acf8ab9a310000be4b03"}, - {file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3d59d125ffbd6d552765510e3f31ed75ebac2c7470c7274195b9161a32350284"}, - {file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8cda06946eac330cbe6598f77bb54e690b4ca93f593dee1568ad22b04f347c15"}, - {file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:07afec21bbbbf8a5cc3651aa96b980afe2526e7f048fdfb7f1014d84acc8b6d8"}, - {file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6b40e8d38afe634559e398cc32b1472f376a4099c75fe6299ae607e404c033b2"}, - {file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:b8dcd239c743aa2f9c22ce674a145e0a25cb1566c495928440a181ca1ccf6719"}, - {file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:84450ba661fb96e9fd67629b93d2941c871ca86fc38d835d19d4225ff946a631"}, - {file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:44aeb140295a2f0659e113b31cfe92c9061622cadbc9e2a2f7b8ef6b1e29ef4b"}, - {file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1db4e7fefefd0f548d73e2e2e041f9df5c59e178b4c72fbac4cc6f535cfb1565"}, - {file = "charset_normalizer-3.4.0-cp312-cp312-win32.whl", hash = "sha256:5726cf76c982532c1863fb64d8c6dd0e4c90b6ece9feb06c9f202417a31f7dd7"}, - {file = "charset_normalizer-3.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:b197e7094f232959f8f20541ead1d9862ac5ebea1d58e9849c1bf979255dfac9"}, - {file = "charset_normalizer-3.4.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:dd4eda173a9fcccb5f2e2bd2a9f423d180194b1bf17cf59e3269899235b2a114"}, - {file = "charset_normalizer-3.4.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e9e3c4c9e1ed40ea53acf11e2a386383c3304212c965773704e4603d589343ed"}, - {file = "charset_normalizer-3.4.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:92a7e36b000bf022ef3dbb9c46bfe2d52c047d5e3f3343f43204263c5addc250"}, - {file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:54b6a92d009cbe2fb11054ba694bc9e284dad30a26757b1e372a1fdddaf21920"}, - {file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ffd9493de4c922f2a38c2bf62b831dcec90ac673ed1ca182fe11b4d8e9f2a64"}, - {file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:35c404d74c2926d0287fbd63ed5d27eb911eb9e4a3bb2c6d294f3cfd4a9e0c23"}, - {file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4796efc4faf6b53a18e3d46343535caed491776a22af773f366534056c4e1fbc"}, - {file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e7fdd52961feb4c96507aa649550ec2a0d527c086d284749b2f582f2d40a2e0d"}, - {file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:92db3c28b5b2a273346bebb24857fda45601aef6ae1c011c0a997106581e8a88"}, - {file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ab973df98fc99ab39080bfb0eb3a925181454d7c3ac8a1e695fddfae696d9e90"}, - {file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:4b67fdab07fdd3c10bb21edab3cbfe8cf5696f453afce75d815d9d7223fbe88b"}, - {file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:aa41e526a5d4a9dfcfbab0716c7e8a1b215abd3f3df5a45cf18a12721d31cb5d"}, - {file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ffc519621dce0c767e96b9c53f09c5d215578e10b02c285809f76509a3931482"}, - {file = "charset_normalizer-3.4.0-cp313-cp313-win32.whl", hash = "sha256:f19c1585933c82098c2a520f8ec1227f20e339e33aca8fa6f956f6691b784e67"}, - {file = "charset_normalizer-3.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:707b82d19e65c9bd28b81dde95249b07bf9f5b90ebe1ef17d9b57473f8a64b7b"}, - {file = "charset_normalizer-3.4.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:dbe03226baf438ac4fda9e2d0715022fd579cb641c4cf639fa40d53b2fe6f3e2"}, - {file = "charset_normalizer-3.4.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dd9a8bd8900e65504a305bf8ae6fa9fbc66de94178c420791d0293702fce2df7"}, - {file = "charset_normalizer-3.4.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b8831399554b92b72af5932cdbbd4ddc55c55f631bb13ff8fe4e6536a06c5c51"}, - {file = "charset_normalizer-3.4.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a14969b8691f7998e74663b77b4c36c0337cb1df552da83d5c9004a93afdb574"}, - {file = "charset_normalizer-3.4.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dcaf7c1524c0542ee2fc82cc8ec337f7a9f7edee2532421ab200d2b920fc97cf"}, - {file = "charset_normalizer-3.4.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:425c5f215d0eecee9a56cdb703203dda90423247421bf0d67125add85d0c4455"}, - {file = "charset_normalizer-3.4.0-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:d5b054862739d276e09928de37c79ddeec42a6e1bfc55863be96a36ba22926f6"}, - {file = "charset_normalizer-3.4.0-cp37-cp37m-musllinux_1_2_i686.whl", hash = "sha256:f3e73a4255342d4eb26ef6df01e3962e73aa29baa3124a8e824c5d3364a65748"}, - {file = "charset_normalizer-3.4.0-cp37-cp37m-musllinux_1_2_ppc64le.whl", hash = "sha256:2f6c34da58ea9c1a9515621f4d9ac379871a8f21168ba1b5e09d74250de5ad62"}, - {file = "charset_normalizer-3.4.0-cp37-cp37m-musllinux_1_2_s390x.whl", hash = "sha256:f09cb5a7bbe1ecae6e87901a2eb23e0256bb524a79ccc53eb0b7629fbe7677c4"}, - {file = "charset_normalizer-3.4.0-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:0099d79bdfcf5c1f0c2c72f91516702ebf8b0b8ddd8905f97a8aecf49712c621"}, - {file = "charset_normalizer-3.4.0-cp37-cp37m-win32.whl", hash = "sha256:9c98230f5042f4945f957d006edccc2af1e03ed5e37ce7c373f00a5a4daa6149"}, - {file = "charset_normalizer-3.4.0-cp37-cp37m-win_amd64.whl", hash = "sha256:62f60aebecfc7f4b82e3f639a7d1433a20ec32824db2199a11ad4f5e146ef5ee"}, - {file = "charset_normalizer-3.4.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:af73657b7a68211996527dbfeffbb0864e043d270580c5aef06dc4b659a4b578"}, - {file = "charset_normalizer-3.4.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:cab5d0b79d987c67f3b9e9c53f54a61360422a5a0bc075f43cab5621d530c3b6"}, - {file = "charset_normalizer-3.4.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:9289fd5dddcf57bab41d044f1756550f9e7cf0c8e373b8cdf0ce8773dc4bd417"}, - {file = "charset_normalizer-3.4.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6b493a043635eb376e50eedf7818f2f322eabbaa974e948bd8bdd29eb7ef2a51"}, - {file = "charset_normalizer-3.4.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9fa2566ca27d67c86569e8c85297aaf413ffab85a8960500f12ea34ff98e4c41"}, - {file = "charset_normalizer-3.4.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a8e538f46104c815be19c975572d74afb53f29650ea2025bbfaef359d2de2f7f"}, - {file = "charset_normalizer-3.4.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6fd30dc99682dc2c603c2b315bded2799019cea829f8bf57dc6b61efde6611c8"}, - {file = "charset_normalizer-3.4.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2006769bd1640bdf4d5641c69a3d63b71b81445473cac5ded39740a226fa88ab"}, - {file = "charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:dc15e99b2d8a656f8e666854404f1ba54765871104e50c8e9813af8a7db07f12"}, - {file = "charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:ab2e5bef076f5a235c3774b4f4028a680432cded7cad37bba0fd90d64b187d19"}, - {file = "charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:4ec9dd88a5b71abfc74e9df5ebe7921c35cbb3b641181a531ca65cdb5e8e4dea"}, - {file = "charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:43193c5cda5d612f247172016c4bb71251c784d7a4d9314677186a838ad34858"}, - {file = "charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:aa693779a8b50cd97570e5a0f343538a8dbd3e496fa5dcb87e29406ad0299654"}, - {file = "charset_normalizer-3.4.0-cp38-cp38-win32.whl", hash = "sha256:7706f5850360ac01d80c89bcef1640683cc12ed87f42579dab6c5d3ed6888613"}, - {file = "charset_normalizer-3.4.0-cp38-cp38-win_amd64.whl", hash = "sha256:c3e446d253bd88f6377260d07c895816ebf33ffffd56c1c792b13bff9c3e1ade"}, - {file = "charset_normalizer-3.4.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:980b4f289d1d90ca5efcf07958d3eb38ed9c0b7676bf2831a54d4f66f9c27dfa"}, - {file = "charset_normalizer-3.4.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f28f891ccd15c514a0981f3b9db9aa23d62fe1a99997512b0491d2ed323d229a"}, - {file = "charset_normalizer-3.4.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a8aacce6e2e1edcb6ac625fb0f8c3a9570ccc7bfba1f63419b3769ccf6a00ed0"}, - {file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bd7af3717683bea4c87acd8c0d3d5b44d56120b26fd3f8a692bdd2d5260c620a"}, - {file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5ff2ed8194587faf56555927b3aa10e6fb69d931e33953943bc4f837dfee2242"}, - {file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e91f541a85298cf35433bf66f3fab2a4a2cff05c127eeca4af174f6d497f0d4b"}, - {file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:309a7de0a0ff3040acaebb35ec45d18db4b28232f21998851cfa709eeff49d62"}, - {file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:285e96d9d53422efc0d7a17c60e59f37fbf3dfa942073f666db4ac71e8d726d0"}, - {file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:5d447056e2ca60382d460a604b6302d8db69476fd2015c81e7c35417cfabe4cd"}, - {file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:20587d20f557fe189b7947d8e7ec5afa110ccf72a3128d61a2a387c3313f46be"}, - {file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:130272c698667a982a5d0e626851ceff662565379baf0ff2cc58067b81d4f11d"}, - {file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:ab22fbd9765e6954bc0bcff24c25ff71dcbfdb185fcdaca49e81bac68fe724d3"}, - {file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:7782afc9b6b42200f7362858f9e73b1f8316afb276d316336c0ec3bd73312742"}, - {file = "charset_normalizer-3.4.0-cp39-cp39-win32.whl", hash = "sha256:2de62e8801ddfff069cd5c504ce3bc9672b23266597d4e4f50eda28846c322f2"}, - {file = "charset_normalizer-3.4.0-cp39-cp39-win_amd64.whl", hash = "sha256:95c3c157765b031331dd4db3c775e58deaee050a3042fcad72cbc4189d7c8dca"}, - {file = "charset_normalizer-3.4.0-py3-none-any.whl", hash = "sha256:fe9f97feb71aa9896b81973a7bbada8c49501dc73e58a10fcef6663af95e5079"}, - {file = "charset_normalizer-3.4.0.tar.gz", hash = "sha256:223217c3d4f82c3ac5e29032b3f1c2eb0fb591b72161f86d93f5719079dae93e"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:91b36a978b5ae0ee86c394f5a54d6ef44db1de0815eb43de826d41d21e4af3de"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7461baadb4dc00fd9e0acbe254e3d7d2112e7f92ced2adc96e54ef6501c5f176"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e218488cd232553829be0664c2292d3af2eeeb94b32bea483cf79ac6a694e037"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:80ed5e856eb7f30115aaf94e4a08114ccc8813e6ed1b5efa74f9f82e8509858f"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b010a7a4fd316c3c484d482922d13044979e78d1861f0e0650423144c616a46a"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4532bff1b8421fd0a320463030c7520f56a79c9024a4e88f01c537316019005a"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d973f03c0cb71c5ed99037b870f2be986c3c05e63622c017ea9816881d2dd247"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:3a3bd0dcd373514dcec91c411ddb9632c0d7d92aed7093b8c3bbb6d69ca74408"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:d9c3cdf5390dcd29aa8056d13e8e99526cda0305acc038b96b30352aff5ff2bb"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:2bdfe3ac2e1bbe5b59a1a63721eb3b95fc9b6817ae4a46debbb4e11f6232428d"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:eab677309cdb30d047996b36d34caeda1dc91149e4fdca0b1a039b3f79d9a807"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-win32.whl", hash = "sha256:c0429126cf75e16c4f0ad00ee0eae4242dc652290f940152ca8c75c3a4b6ee8f"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:9f0b8b1c6d84c8034a44893aba5e767bf9c7a211e313a9605d9c617d7083829f"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8bfa33f4f2672964266e940dd22a195989ba31669bd84629f05fab3ef4e2d125"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:28bf57629c75e810b6ae989f03c0828d64d6b26a5e205535585f96093e405ed1"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f08ff5e948271dc7e18a35641d2f11a4cd8dfd5634f55228b691e62b37125eb3"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:234ac59ea147c59ee4da87a0c0f098e9c8d169f4dc2a159ef720f1a61bbe27cd"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd4ec41f914fa74ad1b8304bbc634b3de73d2a0889bd32076342a573e0779e00"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eea6ee1db730b3483adf394ea72f808b6e18cf3cb6454b4d86e04fa8c4327a12"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c96836c97b1238e9c9e3fe90844c947d5afbf4f4c92762679acfe19927d81d77"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:4d86f7aff21ee58f26dcf5ae81a9addbd914115cdebcbb2217e4f0ed8982e146"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:09b5e6733cbd160dcc09589227187e242a30a49ca5cefa5a7edd3f9d19ed53fd"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:5777ee0881f9499ed0f71cc82cf873d9a0ca8af166dfa0af8ec4e675b7df48e6"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:237bdbe6159cff53b4f24f397d43c6336c6b0b42affbe857970cefbb620911c8"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-win32.whl", hash = "sha256:8417cb1f36cc0bc7eaba8ccb0e04d55f0ee52df06df3ad55259b9a323555fc8b"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:d7f50a1f8c450f3925cb367d011448c39239bb3eb4117c36a6d354794de4ce76"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:73d94b58ec7fecbc7366247d3b0b10a21681004153238750bb67bd9012414545"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dad3e487649f498dd991eeb901125411559b22e8d7ab25d3aeb1af367df5efd7"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c30197aa96e8eed02200a83fba2657b4c3acd0f0aa4bdc9f6c1af8e8962e0757"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2369eea1ee4a7610a860d88f268eb39b95cb588acd7235e02fd5a5601773d4fa"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc2722592d8998c870fa4e290c2eec2c1569b87fe58618e67d38b4665dfa680d"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffc9202a29ab3920fa812879e95a9e78b2465fd10be7fcbd042899695d75e616"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:804a4d582ba6e5b747c625bf1255e6b1507465494a40a2130978bda7b932c90b"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:0f55e69f030f7163dffe9fd0752b32f070566451afe180f99dbeeb81f511ad8d"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c4c3e6da02df6fa1410a7680bd3f63d4f710232d3139089536310d027950696a"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:5df196eb874dae23dcfb968c83d4f8fdccb333330fe1fc278ac5ceeb101003a9"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e358e64305fe12299a08e08978f51fc21fac060dcfcddd95453eabe5b93ed0e1"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-win32.whl", hash = "sha256:9b23ca7ef998bc739bf6ffc077c2116917eabcc901f88da1b9856b210ef63f35"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:6ff8a4a60c227ad87030d76e99cd1698345d4491638dfa6673027c48b3cd395f"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:aabfa34badd18f1da5ec1bc2715cadc8dca465868a4e73a0173466b688f29dda"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22e14b5d70560b8dd51ec22863f370d1e595ac3d024cb8ad7d308b4cd95f8313"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8436c508b408b82d87dc5f62496973a1805cd46727c34440b0d29d8a2f50a6c9"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2d074908e1aecee37a7635990b2c6d504cd4766c7bc9fc86d63f9c09af3fa11b"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:955f8851919303c92343d2f66165294848d57e9bba6cf6e3625485a70a038d11"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:44ecbf16649486d4aebafeaa7ec4c9fed8b88101f4dd612dcaf65d5e815f837f"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0924e81d3d5e70f8126529951dac65c1010cdf117bb75eb02dd12339b57749dd"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2967f74ad52c3b98de4c3b32e1a44e32975e008a9cd2a8cc8966d6a5218c5cb2"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c75cb2a3e389853835e84a2d8fb2b81a10645b503eca9bcb98df6b5a43eb8886"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:09b26ae6b1abf0d27570633b2b078a2a20419c99d66fb2823173d73f188ce601"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fa88b843d6e211393a37219e6a1c1df99d35e8fd90446f1118f4216e307e48cd"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-win32.whl", hash = "sha256:eb8178fe3dba6450a3e024e95ac49ed3400e506fd4e9e5c32d30adda88cbd407"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:b1ac5992a838106edb89654e0aebfc24f5848ae2547d22c2c3f66454daa11971"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f30bf9fd9be89ecb2360c7d94a711f00c09b976258846efe40db3d05828e8089"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:97f68b8d6831127e4787ad15e6757232e14e12060bec17091b85eb1486b91d8d"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7974a0b5ecd505609e3b19742b60cee7aa2aa2fb3151bc917e6e2646d7667dcf"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc54db6c8593ef7d4b2a331b58653356cf04f67c960f584edb7c3d8c97e8f39e"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:311f30128d7d333eebd7896965bfcfbd0065f1716ec92bd5638d7748eb6f936a"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:7d053096f67cd1241601111b698f5cad775f97ab25d81567d3f59219b5f1adbd"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_i686.whl", hash = "sha256:807f52c1f798eef6cf26beb819eeb8819b1622ddfeef9d0977a8502d4db6d534"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_ppc64le.whl", hash = "sha256:dccbe65bd2f7f7ec22c4ff99ed56faa1e9f785482b9bbd7c717e26fd723a1d1e"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_s390x.whl", hash = "sha256:2fb9bd477fdea8684f78791a6de97a953c51831ee2981f8e4f583ff3b9d9687e"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:01732659ba9b5b873fc117534143e4feefecf3b2078b0a6a2e925271bb6f4cfa"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-win32.whl", hash = "sha256:7a4f97a081603d2050bfaffdefa5b02a9ec823f8348a572e39032caa8404a487"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-win_amd64.whl", hash = "sha256:7b1bef6280950ee6c177b326508f86cad7ad4dff12454483b51d8b7d673a2c5d"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:ecddf25bee22fe4fe3737a399d0d177d72bc22be6913acfab364b40bce1ba83c"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c60ca7339acd497a55b0ea5d506b2a2612afb2826560416f6894e8b5770d4a9"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b7b2d86dd06bfc2ade3312a83a5c364c7ec2e3498f8734282c6c3d4b07b346b8"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dd78cfcda14a1ef52584dbb008f7ac81c1328c0f58184bf9a84c49c605002da6"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e27f48bcd0957c6d4cb9d6fa6b61d192d0b13d5ef563e5f2ae35feafc0d179c"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:01ad647cdd609225c5350561d084b42ddf732f4eeefe6e678765636791e78b9a"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:619a609aa74ae43d90ed2e89bdd784765de0a25ca761b93e196d938b8fd1dbbd"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:89149166622f4db9b4b6a449256291dc87a99ee53151c74cbd82a53c8c2f6ccd"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:7709f51f5f7c853f0fb938bcd3bc59cdfdc5203635ffd18bf354f6967ea0f824"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:345b0426edd4e18138d6528aed636de7a9ed169b4aaf9d61a8c19e39d26838ca"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:0907f11d019260cdc3f94fbdb23ff9125f6b5d1039b76003b5b0ac9d6a6c9d5b"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-win32.whl", hash = "sha256:ea0d8d539afa5eb2728aa1932a988a9a7af94f18582ffae4bc10b3fbdad0626e"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:329ce159e82018d646c7ac45b01a430369d526569ec08516081727a20e9e4af4"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:b97e690a2118911e39b4042088092771b4ae3fc3aa86518f84b8cf6888dbdb41"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:78baa6d91634dfb69ec52a463534bc0df05dbd546209b79a3880a34487f4b84f"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1a2bc9f351a75ef49d664206d51f8e5ede9da246602dc2d2726837620ea034b2"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:75832c08354f595c760a804588b9357d34ec00ba1c940c15e31e96d902093770"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0af291f4fe114be0280cdd29d533696a77b5b49cfde5467176ecab32353395c4"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0167ddc8ab6508fe81860a57dd472b2ef4060e8d378f0cc555707126830f2537"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:2a75d49014d118e4198bcee5ee0a6f25856b29b12dbf7cd012791f8a6cc5c496"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:363e2f92b0f0174b2f8238240a1a30142e3db7b957a5dd5689b0e75fb717cc78"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:ab36c8eb7e454e34e60eb55ca5d241a5d18b2c6244f6827a30e451c42410b5f7"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:4c0907b1928a36d5a998d72d64d8eaa7244989f7aaaf947500d3a800c83a3fd6"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:04432ad9479fa40ec0f387795ddad4437a2b50417c69fa275e212933519ff294"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-win32.whl", hash = "sha256:3bed14e9c89dcb10e8f3a29f9ccac4955aebe93c71ae803af79265c9ca5644c5"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:49402233c892a461407c512a19435d1ce275543138294f7ef013f0b63d5d3765"}, + {file = "charset_normalizer-3.4.1-py3-none-any.whl", hash = "sha256:d98b1668f06378c6dbefec3b92299716b931cd4e6061f3c875a71ced1780ab85"}, + {file = "charset_normalizer-3.4.1.tar.gz", hash = "sha256:44251f18cd68a75b56585dd00dae26183e102cd5e0f9f1466e6df5da2ed64ea3"}, ] [[package]] @@ -937,17 +924,6 @@ files = [ {file = "distlib-0.3.9.tar.gz", hash = "sha256:a60f20dea646b8a33f3e7772f74dc0b2d0772d2837ee1342a00645c81edf9403"}, ] -[[package]] -name = "distro" -version = "1.9.0" -description = "Distro - an OS platform information API" -optional = false -python-versions = ">=3.6" -files = [ - {file = "distro-1.9.0-py3-none-any.whl", hash = "sha256:7bffd925d65168f85027d8da9af6bddab658135b840670a223589bc0c8ef02b2"}, - {file = "distro-1.9.0.tar.gz", hash = "sha256:2fa77c6fd8940f116ee1d6b94a2f90b13b5ea8d019b98bc8bafdcabcdd9bdbed"}, -] - [[package]] name = "dj-database-url" version = "2.3.0" @@ -1674,38 +1650,6 @@ files = [ {file = "frozenlist-1.5.0.tar.gz", hash = "sha256:81d5af29e61b9c8348e876d442253723928dce6433e0e76cd925cd83f1b4b817"}, ] -[[package]] -name = "gitdb" -version = "4.0.11" -description = "Git Object Database" -optional = false -python-versions = ">=3.7" -files = [ - {file = "gitdb-4.0.11-py3-none-any.whl", hash = "sha256:81a3407ddd2ee8df444cbacea00e2d038e40150acfa3001696fe0dcf1d3adfa4"}, - {file = "gitdb-4.0.11.tar.gz", hash = "sha256:bf5421126136d6d0af55bc1e7c1af1c397a34f5b7bd79e776cd3e89785c2b04b"}, -] - -[package.dependencies] -smmap = ">=3.0.1,<6" - -[[package]] -name = "gitpython" -version = "3.1.43" -description = "GitPython is a Python library used to interact with Git repositories" -optional = false -python-versions = ">=3.7" -files = [ - {file = "GitPython-3.1.43-py3-none-any.whl", hash = "sha256:eec7ec56b92aad751f9912a73404bc02ba212a23adb2c7098ee668417051a1ff"}, - {file = "GitPython-3.1.43.tar.gz", hash = "sha256:35f314a9f878467f5453cc1fee295c3e18e52f1b99f10f6cf5b1682e968a9e7c"}, -] - -[package.dependencies] -gitdb = ">=4.0.1,<5" - -[package.extras] -doc = ["sphinx (==4.3.2)", "sphinx-autodoc-typehints", "sphinx-rtd-theme", "sphinxcontrib-applehelp (>=1.0.2,<=1.0.4)", "sphinxcontrib-devhelp (==1.0.2)", "sphinxcontrib-htmlhelp (>=2.0.0,<=2.0.1)", "sphinxcontrib-qthelp (==1.0.3)", "sphinxcontrib-serializinghtml (==1.1.5)"] -test = ["coverage[toml]", "ddt (>=1.1.1,!=1.4.3)", "mock", "mypy", "pre-commit", "pytest (>=7.3.1)", "pytest-cov", "pytest-instafail", "pytest-mock", "pytest-sugar", "typing-extensions"] - [[package]] name = "giturlparse-py" version = "0.0.5" @@ -1879,92 +1823,6 @@ protobuf = ">=3.20.2,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4 [package.extras] grpc = ["grpcio (>=1.44.0,<2.0.0.dev0)"] -[[package]] -name = "greenlet" -version = "3.1.1" -description = "Lightweight in-process concurrent programming" -optional = false -python-versions = ">=3.7" -files = [ - {file = "greenlet-3.1.1-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:0bbae94a29c9e5c7e4a2b7f0aae5c17e8e90acbfd3bf6270eeba60c39fce3563"}, - {file = "greenlet-3.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0fde093fb93f35ca72a556cf72c92ea3ebfda3d79fc35bb19fbe685853869a83"}, - {file = "greenlet-3.1.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:36b89d13c49216cadb828db8dfa6ce86bbbc476a82d3a6c397f0efae0525bdd0"}, - {file = "greenlet-3.1.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:94b6150a85e1b33b40b1464a3f9988dcc5251d6ed06842abff82e42632fac120"}, - {file = "greenlet-3.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:93147c513fac16385d1036b7e5b102c7fbbdb163d556b791f0f11eada7ba65dc"}, - {file = "greenlet-3.1.1-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:da7a9bff22ce038e19bf62c4dd1ec8391062878710ded0a845bcf47cc0200617"}, - {file = "greenlet-3.1.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b2795058c23988728eec1f36a4e5e4ebad22f8320c85f3587b539b9ac84128d7"}, - {file = "greenlet-3.1.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:ed10eac5830befbdd0c32f83e8aa6288361597550ba669b04c48f0f9a2c843c6"}, - {file = "greenlet-3.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:77c386de38a60d1dfb8e55b8c1101d68c79dfdd25c7095d51fec2dd800892b80"}, - {file = "greenlet-3.1.1-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:e4d333e558953648ca09d64f13e6d8f0523fa705f51cae3f03b5983489958c70"}, - {file = "greenlet-3.1.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:09fc016b73c94e98e29af67ab7b9a879c307c6731a2c9da0db5a7d9b7edd1159"}, - {file = "greenlet-3.1.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d5e975ca70269d66d17dd995dafc06f1b06e8cb1ec1e9ed54c1d1e4a7c4cf26e"}, - {file = "greenlet-3.1.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3b2813dc3de8c1ee3f924e4d4227999285fd335d1bcc0d2be6dc3f1f6a318ec1"}, - {file = "greenlet-3.1.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e347b3bfcf985a05e8c0b7d462ba6f15b1ee1c909e2dcad795e49e91b152c383"}, - {file = "greenlet-3.1.1-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9e8f8c9cb53cdac7ba9793c276acd90168f416b9ce36799b9b885790f8ad6c0a"}, - {file = "greenlet-3.1.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:62ee94988d6b4722ce0028644418d93a52429e977d742ca2ccbe1c4f4a792511"}, - {file = "greenlet-3.1.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1776fd7f989fc6b8d8c8cb8da1f6b82c5814957264d1f6cf818d475ec2bf6395"}, - {file = "greenlet-3.1.1-cp311-cp311-win_amd64.whl", hash = "sha256:48ca08c771c268a768087b408658e216133aecd835c0ded47ce955381105ba39"}, - {file = "greenlet-3.1.1-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:4afe7ea89de619adc868e087b4d2359282058479d7cfb94970adf4b55284574d"}, - {file = "greenlet-3.1.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f406b22b7c9a9b4f8aa9d2ab13d6ae0ac3e85c9a809bd590ad53fed2bf70dc79"}, - {file = "greenlet-3.1.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c3a701fe5a9695b238503ce5bbe8218e03c3bcccf7e204e455e7462d770268aa"}, - {file = "greenlet-3.1.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2846930c65b47d70b9d178e89c7e1a69c95c1f68ea5aa0a58646b7a96df12441"}, - {file = "greenlet-3.1.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:99cfaa2110534e2cf3ba31a7abcac9d328d1d9f1b95beede58294a60348fba36"}, - {file = "greenlet-3.1.1-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1443279c19fca463fc33e65ef2a935a5b09bb90f978beab37729e1c3c6c25fe9"}, - {file = "greenlet-3.1.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b7cede291382a78f7bb5f04a529cb18e068dd29e0fb27376074b6d0317bf4dd0"}, - {file = "greenlet-3.1.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:23f20bb60ae298d7d8656c6ec6db134bca379ecefadb0b19ce6f19d1f232a942"}, - {file = "greenlet-3.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:7124e16b4c55d417577c2077be379514321916d5790fa287c9ed6f23bd2ffd01"}, - {file = "greenlet-3.1.1-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:05175c27cb459dcfc05d026c4232f9de8913ed006d42713cb8a5137bd49375f1"}, - {file = "greenlet-3.1.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:935e943ec47c4afab8965954bf49bfa639c05d4ccf9ef6e924188f762145c0ff"}, - {file = "greenlet-3.1.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:667a9706c970cb552ede35aee17339a18e8f2a87a51fba2ed39ceeeb1004798a"}, - {file = "greenlet-3.1.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b8a678974d1f3aa55f6cc34dc480169d58f2e6d8958895d68845fa4ab566509e"}, - {file = "greenlet-3.1.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:efc0f674aa41b92da8c49e0346318c6075d734994c3c4e4430b1c3f853e498e4"}, - {file = "greenlet-3.1.1-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0153404a4bb921f0ff1abeb5ce8a5131da56b953eda6e14b88dc6bbc04d2049e"}, - {file = "greenlet-3.1.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:275f72decf9932639c1c6dd1013a1bc266438eb32710016a1c742df5da6e60a1"}, - {file = "greenlet-3.1.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:c4aab7f6381f38a4b42f269057aee279ab0fc7bf2e929e3d4abfae97b682a12c"}, - {file = "greenlet-3.1.1-cp313-cp313-win_amd64.whl", hash = "sha256:b42703b1cf69f2aa1df7d1030b9d77d3e584a70755674d60e710f0af570f3761"}, - {file = "greenlet-3.1.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f1695e76146579f8c06c1509c7ce4dfe0706f49c6831a817ac04eebb2fd02011"}, - {file = "greenlet-3.1.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7876452af029456b3f3549b696bb36a06db7c90747740c5302f74a9e9fa14b13"}, - {file = "greenlet-3.1.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4ead44c85f8ab905852d3de8d86f6f8baf77109f9da589cb4fa142bd3b57b475"}, - {file = "greenlet-3.1.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8320f64b777d00dd7ccdade271eaf0cad6636343293a25074cc5566160e4de7b"}, - {file = "greenlet-3.1.1-cp313-cp313t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6510bf84a6b643dabba74d3049ead221257603a253d0a9873f55f6a59a65f822"}, - {file = "greenlet-3.1.1-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:04b013dc07c96f83134b1e99888e7a79979f1a247e2a9f59697fa14b5862ed01"}, - {file = "greenlet-3.1.1-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:411f015496fec93c1c8cd4e5238da364e1da7a124bcb293f085bf2860c32c6f6"}, - {file = "greenlet-3.1.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:47da355d8687fd65240c364c90a31569a133b7b60de111c255ef5b606f2ae291"}, - {file = "greenlet-3.1.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:98884ecf2ffb7d7fe6bd517e8eb99d31ff7855a840fa6d0d63cd07c037f6a981"}, - {file = "greenlet-3.1.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f1d4aeb8891338e60d1ab6127af1fe45def5259def8094b9c7e34690c8858803"}, - {file = "greenlet-3.1.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db32b5348615a04b82240cc67983cb315309e88d444a288934ee6ceaebcad6cc"}, - {file = "greenlet-3.1.1-cp37-cp37m-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dcc62f31eae24de7f8dce72134c8651c58000d3b1868e01392baea7c32c247de"}, - {file = "greenlet-3.1.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:1d3755bcb2e02de341c55b4fca7a745a24a9e7212ac953f6b3a48d117d7257aa"}, - {file = "greenlet-3.1.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:b8da394b34370874b4572676f36acabac172602abf054cbc4ac910219f3340af"}, - {file = "greenlet-3.1.1-cp37-cp37m-win32.whl", hash = "sha256:a0dfc6c143b519113354e780a50381508139b07d2177cb6ad6a08278ec655798"}, - {file = "greenlet-3.1.1-cp37-cp37m-win_amd64.whl", hash = "sha256:54558ea205654b50c438029505def3834e80f0869a70fb15b871c29b4575ddef"}, - {file = "greenlet-3.1.1-cp38-cp38-macosx_11_0_universal2.whl", hash = "sha256:346bed03fe47414091be4ad44786d1bd8bef0c3fcad6ed3dee074a032ab408a9"}, - {file = "greenlet-3.1.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dfc59d69fc48664bc693842bd57acfdd490acafda1ab52c7836e3fc75c90a111"}, - {file = "greenlet-3.1.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d21e10da6ec19b457b82636209cbe2331ff4306b54d06fa04b7c138ba18c8a81"}, - {file = "greenlet-3.1.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:37b9de5a96111fc15418819ab4c4432e4f3c2ede61e660b1e33971eba26ef9ba"}, - {file = "greenlet-3.1.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6ef9ea3f137e5711f0dbe5f9263e8c009b7069d8a1acea822bd5e9dae0ae49c8"}, - {file = "greenlet-3.1.1-cp38-cp38-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:85f3ff71e2e60bd4b4932a043fbbe0f499e263c628390b285cb599154a3b03b1"}, - {file = "greenlet-3.1.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:95ffcf719966dd7c453f908e208e14cde192e09fde6c7186c8f1896ef778d8cd"}, - {file = "greenlet-3.1.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:03a088b9de532cbfe2ba2034b2b85e82df37874681e8c470d6fb2f8c04d7e4b7"}, - {file = "greenlet-3.1.1-cp38-cp38-win32.whl", hash = "sha256:8b8b36671f10ba80e159378df9c4f15c14098c4fd73a36b9ad715f057272fbef"}, - {file = "greenlet-3.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:7017b2be767b9d43cc31416aba48aab0d2309ee31b4dbf10a1d38fb7972bdf9d"}, - {file = "greenlet-3.1.1-cp39-cp39-macosx_11_0_universal2.whl", hash = "sha256:396979749bd95f018296af156201d6211240e7a23090f50a8d5d18c370084dc3"}, - {file = "greenlet-3.1.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca9d0ff5ad43e785350894d97e13633a66e2b50000e8a183a50a88d834752d42"}, - {file = "greenlet-3.1.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f6ff3b14f2df4c41660a7dec01045a045653998784bf8cfcb5a525bdffffbc8f"}, - {file = "greenlet-3.1.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:94ebba31df2aa506d7b14866fed00ac141a867e63143fe5bca82a8e503b36437"}, - {file = "greenlet-3.1.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:73aaad12ac0ff500f62cebed98d8789198ea0e6f233421059fa68a5aa7220145"}, - {file = "greenlet-3.1.1-cp39-cp39-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:63e4844797b975b9af3a3fb8f7866ff08775f5426925e1e0bbcfe7932059a12c"}, - {file = "greenlet-3.1.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:7939aa3ca7d2a1593596e7ac6d59391ff30281ef280d8632fa03d81f7c5f955e"}, - {file = "greenlet-3.1.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d0028e725ee18175c6e422797c407874da24381ce0690d6b9396c204c7f7276e"}, - {file = "greenlet-3.1.1-cp39-cp39-win32.whl", hash = "sha256:5e06afd14cbaf9e00899fae69b24a32f2196c19de08fcb9f4779dd4f004e5e7c"}, - {file = "greenlet-3.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:3319aa75e0e0639bc15ff54ca327e8dc7a6fe404003496e3c6925cd3142e0e22"}, - {file = "greenlet-3.1.1.tar.gz", hash = "sha256:4ce3ac6cdb6adf7946475d7ef31777c26d94bccc377e070a7986bd2d5c515467"}, -] - -[package.extras] -docs = ["Sphinx", "furo"] -test = ["objgraph", "psutil"] - [[package]] name = "gunicorn" version = "23.0.0" @@ -2131,91 +1989,6 @@ files = [ [package.extras] colors = ["colorama (>=0.4.6)"] -[[package]] -name = "jiter" -version = "0.8.2" -description = "Fast iterable JSON parser." -optional = false -python-versions = ">=3.8" -files = [ - {file = "jiter-0.8.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:ca8577f6a413abe29b079bc30f907894d7eb07a865c4df69475e868d73e71c7b"}, - {file = "jiter-0.8.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b25bd626bde7fb51534190c7e3cb97cee89ee76b76d7585580e22f34f5e3f393"}, - {file = "jiter-0.8.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d5c826a221851a8dc028eb6d7d6429ba03184fa3c7e83ae01cd6d3bd1d4bd17d"}, - {file = "jiter-0.8.2-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d35c864c2dff13dfd79fb070fc4fc6235d7b9b359efe340e1261deb21b9fcb66"}, - {file = "jiter-0.8.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f557c55bc2b7676e74d39d19bcb8775ca295c7a028246175d6a8b431e70835e5"}, - {file = "jiter-0.8.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:580ccf358539153db147e40751a0b41688a5ceb275e6f3e93d91c9467f42b2e3"}, - {file = "jiter-0.8.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:af102d3372e917cffce49b521e4c32c497515119dc7bd8a75665e90a718bbf08"}, - {file = "jiter-0.8.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:cadcc978f82397d515bb2683fc0d50103acff2a180552654bb92d6045dec2c49"}, - {file = "jiter-0.8.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:ba5bdf56969cad2019d4e8ffd3f879b5fdc792624129741d3d83fc832fef8c7d"}, - {file = "jiter-0.8.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:3b94a33a241bee9e34b8481cdcaa3d5c2116f575e0226e421bed3f7a6ea71cff"}, - {file = "jiter-0.8.2-cp310-cp310-win32.whl", hash = "sha256:6e5337bf454abddd91bd048ce0dca5134056fc99ca0205258766db35d0a2ea43"}, - {file = "jiter-0.8.2-cp310-cp310-win_amd64.whl", hash = "sha256:4a9220497ca0cb1fe94e3f334f65b9b5102a0b8147646118f020d8ce1de70105"}, - {file = "jiter-0.8.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:2dd61c5afc88a4fda7d8b2cf03ae5947c6ac7516d32b7a15bf4b49569a5c076b"}, - {file = "jiter-0.8.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a6c710d657c8d1d2adbbb5c0b0c6bfcec28fd35bd6b5f016395f9ac43e878a15"}, - {file = "jiter-0.8.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a9584de0cd306072635fe4b89742bf26feae858a0683b399ad0c2509011b9dc0"}, - {file = "jiter-0.8.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5a90a923338531b7970abb063cfc087eebae6ef8ec8139762007188f6bc69a9f"}, - {file = "jiter-0.8.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d21974d246ed0181558087cd9f76e84e8321091ebfb3a93d4c341479a736f099"}, - {file = "jiter-0.8.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:32475a42b2ea7b344069dc1e81445cfc00b9d0e3ca837f0523072432332e9f74"}, - {file = "jiter-0.8.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8b9931fd36ee513c26b5bf08c940b0ac875de175341cbdd4fa3be109f0492586"}, - {file = "jiter-0.8.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ce0820f4a3a59ddced7fce696d86a096d5cc48d32a4183483a17671a61edfddc"}, - {file = "jiter-0.8.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:8ffc86ae5e3e6a93765d49d1ab47b6075a9c978a2b3b80f0f32628f39caa0c88"}, - {file = "jiter-0.8.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5127dc1abd809431172bc3fbe8168d6b90556a30bb10acd5ded41c3cfd6f43b6"}, - {file = "jiter-0.8.2-cp311-cp311-win32.whl", hash = "sha256:66227a2c7b575720c1871c8800d3a0122bb8ee94edb43a5685aa9aceb2782d44"}, - {file = "jiter-0.8.2-cp311-cp311-win_amd64.whl", hash = "sha256:cde031d8413842a1e7501e9129b8e676e62a657f8ec8166e18a70d94d4682855"}, - {file = "jiter-0.8.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:e6ec2be506e7d6f9527dae9ff4b7f54e68ea44a0ef6b098256ddf895218a2f8f"}, - {file = "jiter-0.8.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:76e324da7b5da060287c54f2fabd3db5f76468006c811831f051942bf68c9d44"}, - {file = "jiter-0.8.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:180a8aea058f7535d1c84183c0362c710f4750bef66630c05f40c93c2b152a0f"}, - {file = "jiter-0.8.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:025337859077b41548bdcbabe38698bcd93cfe10b06ff66617a48ff92c9aec60"}, - {file = "jiter-0.8.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ecff0dc14f409599bbcafa7e470c00b80f17abc14d1405d38ab02e4b42e55b57"}, - {file = "jiter-0.8.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ffd9fee7d0775ebaba131f7ca2e2d83839a62ad65e8e02fe2bd8fc975cedeb9e"}, - {file = "jiter-0.8.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:14601dcac4889e0a1c75ccf6a0e4baf70dbc75041e51bcf8d0e9274519df6887"}, - {file = "jiter-0.8.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:92249669925bc1c54fcd2ec73f70f2c1d6a817928480ee1c65af5f6b81cdf12d"}, - {file = "jiter-0.8.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:e725edd0929fa79f8349ab4ec7f81c714df51dc4e991539a578e5018fa4a7152"}, - {file = "jiter-0.8.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:bf55846c7b7a680eebaf9c3c48d630e1bf51bdf76c68a5f654b8524335b0ad29"}, - {file = "jiter-0.8.2-cp312-cp312-win32.whl", hash = "sha256:7efe4853ecd3d6110301665a5178b9856be7e2a9485f49d91aa4d737ad2ae49e"}, - {file = "jiter-0.8.2-cp312-cp312-win_amd64.whl", hash = "sha256:83c0efd80b29695058d0fd2fa8a556490dbce9804eac3e281f373bbc99045f6c"}, - {file = "jiter-0.8.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:ca1f08b8e43dc3bd0594c992fb1fd2f7ce87f7bf0d44358198d6da8034afdf84"}, - {file = "jiter-0.8.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5672a86d55416ccd214c778efccf3266b84f87b89063b582167d803246354be4"}, - {file = "jiter-0.8.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:58dc9bc9767a1101f4e5e22db1b652161a225874d66f0e5cb8e2c7d1c438b587"}, - {file = "jiter-0.8.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:37b2998606d6dadbb5ccda959a33d6a5e853252d921fec1792fc902351bb4e2c"}, - {file = "jiter-0.8.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4ab9a87f3784eb0e098f84a32670cfe4a79cb6512fd8f42ae3d0709f06405d18"}, - {file = "jiter-0.8.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:79aec8172b9e3c6d05fd4b219d5de1ac616bd8da934107325a6c0d0e866a21b6"}, - {file = "jiter-0.8.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:711e408732d4e9a0208008e5892c2966b485c783cd2d9a681f3eb147cf36c7ef"}, - {file = "jiter-0.8.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:653cf462db4e8c41995e33d865965e79641ef45369d8a11f54cd30888b7e6ff1"}, - {file = "jiter-0.8.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:9c63eaef32b7bebac8ebebf4dabebdbc6769a09c127294db6babee38e9f405b9"}, - {file = "jiter-0.8.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:eb21aaa9a200d0a80dacc7a81038d2e476ffe473ffdd9c91eb745d623561de05"}, - {file = "jiter-0.8.2-cp313-cp313-win32.whl", hash = "sha256:789361ed945d8d42850f919342a8665d2dc79e7e44ca1c97cc786966a21f627a"}, - {file = "jiter-0.8.2-cp313-cp313-win_amd64.whl", hash = "sha256:ab7f43235d71e03b941c1630f4b6e3055d46b6cb8728a17663eaac9d8e83a865"}, - {file = "jiter-0.8.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:b426f72cd77da3fec300ed3bc990895e2dd6b49e3bfe6c438592a3ba660e41ca"}, - {file = "jiter-0.8.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b2dd880785088ff2ad21ffee205e58a8c1ddabc63612444ae41e5e4b321b39c0"}, - {file = "jiter-0.8.2-cp313-cp313t-win_amd64.whl", hash = "sha256:3ac9f578c46f22405ff7f8b1f5848fb753cc4b8377fbec8470a7dc3997ca7566"}, - {file = "jiter-0.8.2-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:9e1fa156ee9454642adb7e7234a383884452532bc9d53d5af2d18d98ada1d79c"}, - {file = "jiter-0.8.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:0cf5dfa9956d96ff2efb0f8e9c7d055904012c952539a774305aaaf3abdf3d6c"}, - {file = "jiter-0.8.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e52bf98c7e727dd44f7c4acb980cb988448faeafed8433c867888268899b298b"}, - {file = "jiter-0.8.2-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a2ecaa3c23e7a7cf86d00eda3390c232f4d533cd9ddea4b04f5d0644faf642c5"}, - {file = "jiter-0.8.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:08d4c92bf480e19fc3f2717c9ce2aa31dceaa9163839a311424b6862252c943e"}, - {file = "jiter-0.8.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:99d9a1eded738299ba8e106c6779ce5c3893cffa0e32e4485d680588adae6db8"}, - {file = "jiter-0.8.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d20be8b7f606df096e08b0b1b4a3c6f0515e8dac296881fe7461dfa0fb5ec817"}, - {file = "jiter-0.8.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d33f94615fcaf872f7fd8cd98ac3b429e435c77619777e8a449d9d27e01134d1"}, - {file = "jiter-0.8.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:317b25e98a35ffec5c67efe56a4e9970852632c810d35b34ecdd70cc0e47b3b6"}, - {file = "jiter-0.8.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:fc9043259ee430ecd71d178fccabd8c332a3bf1e81e50cae43cc2b28d19e4cb7"}, - {file = "jiter-0.8.2-cp38-cp38-win32.whl", hash = "sha256:fc5adda618205bd4678b146612ce44c3cbfdee9697951f2c0ffdef1f26d72b63"}, - {file = "jiter-0.8.2-cp38-cp38-win_amd64.whl", hash = "sha256:cd646c827b4f85ef4a78e4e58f4f5854fae0caf3db91b59f0d73731448a970c6"}, - {file = "jiter-0.8.2-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:e41e75344acef3fc59ba4765df29f107f309ca9e8eace5baacabd9217e52a5ee"}, - {file = "jiter-0.8.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:7f22b16b35d5c1df9dfd58843ab2cd25e6bf15191f5a236bed177afade507bfc"}, - {file = "jiter-0.8.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f7200b8f7619d36aa51c803fd52020a2dfbea36ffec1b5e22cab11fd34d95a6d"}, - {file = "jiter-0.8.2-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:70bf4c43652cc294040dbb62256c83c8718370c8b93dd93d934b9a7bf6c4f53c"}, - {file = "jiter-0.8.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f9d471356dc16f84ed48768b8ee79f29514295c7295cb41e1133ec0b2b8d637d"}, - {file = "jiter-0.8.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:859e8eb3507894093d01929e12e267f83b1d5f6221099d3ec976f0c995cb6bd9"}, - {file = "jiter-0.8.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eaa58399c01db555346647a907b4ef6d4f584b123943be6ed5588c3f2359c9f4"}, - {file = "jiter-0.8.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8f2d5ed877f089862f4c7aacf3a542627c1496f972a34d0474ce85ee7d939c27"}, - {file = "jiter-0.8.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:03c9df035d4f8d647f8c210ddc2ae0728387275340668fb30d2421e17d9a0841"}, - {file = "jiter-0.8.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8bd2a824d08d8977bb2794ea2682f898ad3d8837932e3a74937e93d62ecbb637"}, - {file = "jiter-0.8.2-cp39-cp39-win32.whl", hash = "sha256:ca29b6371ebc40e496995c94b988a101b9fbbed48a51190a4461fcb0a68b4a36"}, - {file = "jiter-0.8.2-cp39-cp39-win_amd64.whl", hash = "sha256:1c0dfbd1be3cbefc7510102370d86e35d1d53e5a93d48519688b1bf0f761160a"}, - {file = "jiter-0.8.2.tar.gz", hash = "sha256:cd73d3e740666d0e639f678adb176fad25c1bcbdae88d8d7b857e1783bb4212d"}, -] - [[package]] name = "joblib" version = "1.4.2" @@ -2227,20 +2000,6 @@ files = [ {file = "joblib-1.4.2.tar.gz", hash = "sha256:2382c5816b2636fbd20a09e0f4e9dad4736765fdfb7dca582943b9c1366b3f0e"}, ] -[[package]] -name = "jsonpatch" -version = "1.33" -description = "Apply JSON-Patches (RFC 6902)" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*, !=3.6.*" -files = [ - {file = "jsonpatch-1.33-py2.py3-none-any.whl", hash = "sha256:0ae28c0cd062bbd8b8ecc26d7d164fbbea9652a1a3693f3b956c1eae5145dade"}, - {file = "jsonpatch-1.33.tar.gz", hash = "sha256:9fcd4009c41e6d12348b4a0ff2563ba56a2923a7dfee731d004e212e1ee5030c"}, -] - -[package.dependencies] -jsonpointer = ">=1.9" - [[package]] name = "jsonpath-python" version = "1.0.6" @@ -2252,236 +2011,95 @@ files = [ {file = "jsonpath_python-1.0.6-py3-none-any.whl", hash = "sha256:1e3b78df579f5efc23565293612decee04214609208a2335884b3ee3f786b575"}, ] -[[package]] -name = "jsonpointer" -version = "3.0.0" -description = "Identify specific nodes in a JSON document (RFC 6901)" -optional = false -python-versions = ">=3.7" -files = [ - {file = "jsonpointer-3.0.0-py2.py3-none-any.whl", hash = "sha256:13e088adc14fca8b6aa8177c044e12701e6ad4b28ff10e65f2267a90109c9942"}, - {file = "jsonpointer-3.0.0.tar.gz", hash = "sha256:2b2d729f2091522d61c3b31f82e11870f60b68f43fbc705cb76bf4b832af59ef"}, -] - [[package]] name = "kiwisolver" -version = "1.4.7" +version = "1.4.8" description = "A fast implementation of the Cassowary constraint solver" optional = false -python-versions = ">=3.8" -files = [ - {file = "kiwisolver-1.4.7-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:8a9c83f75223d5e48b0bc9cb1bf2776cf01563e00ade8775ffe13b0b6e1af3a6"}, - {file = "kiwisolver-1.4.7-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:58370b1ffbd35407444d57057b57da5d6549d2d854fa30249771775c63b5fe17"}, - {file = "kiwisolver-1.4.7-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:aa0abdf853e09aff551db11fce173e2177d00786c688203f52c87ad7fcd91ef9"}, - {file = "kiwisolver-1.4.7-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:8d53103597a252fb3ab8b5845af04c7a26d5e7ea8122303dd7a021176a87e8b9"}, - {file = "kiwisolver-1.4.7-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:88f17c5ffa8e9462fb79f62746428dd57b46eb931698e42e990ad63103f35e6c"}, - {file = "kiwisolver-1.4.7-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:88a9ca9c710d598fd75ee5de59d5bda2684d9db36a9f50b6125eaea3969c2599"}, - {file = "kiwisolver-1.4.7-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f4d742cb7af1c28303a51b7a27aaee540e71bb8e24f68c736f6f2ffc82f2bf05"}, - {file = "kiwisolver-1.4.7-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e28c7fea2196bf4c2f8d46a0415c77a1c480cc0724722f23d7410ffe9842c407"}, - {file = "kiwisolver-1.4.7-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:e968b84db54f9d42046cf154e02911e39c0435c9801681e3fc9ce8a3c4130278"}, - {file = "kiwisolver-1.4.7-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:0c18ec74c0472de033e1bebb2911c3c310eef5649133dd0bedf2a169a1b269e5"}, - {file = "kiwisolver-1.4.7-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:8f0ea6da6d393d8b2e187e6a5e3fb81f5862010a40c3945e2c6d12ae45cfb2ad"}, - {file = "kiwisolver-1.4.7-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:f106407dda69ae456dd1227966bf445b157ccc80ba0dff3802bb63f30b74e895"}, - {file = "kiwisolver-1.4.7-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:84ec80df401cfee1457063732d90022f93951944b5b58975d34ab56bb150dfb3"}, - {file = "kiwisolver-1.4.7-cp310-cp310-win32.whl", hash = "sha256:71bb308552200fb2c195e35ef05de12f0c878c07fc91c270eb3d6e41698c3bcc"}, - {file = "kiwisolver-1.4.7-cp310-cp310-win_amd64.whl", hash = "sha256:44756f9fd339de0fb6ee4f8c1696cfd19b2422e0d70b4cefc1cc7f1f64045a8c"}, - {file = "kiwisolver-1.4.7-cp310-cp310-win_arm64.whl", hash = "sha256:78a42513018c41c2ffd262eb676442315cbfe3c44eed82385c2ed043bc63210a"}, - {file = "kiwisolver-1.4.7-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:d2b0e12a42fb4e72d509fc994713d099cbb15ebf1103545e8a45f14da2dfca54"}, - {file = "kiwisolver-1.4.7-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2a8781ac3edc42ea4b90bc23e7d37b665d89423818e26eb6df90698aa2287c95"}, - {file = "kiwisolver-1.4.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:46707a10836894b559e04b0fd143e343945c97fd170d69a2d26d640b4e297935"}, - {file = "kiwisolver-1.4.7-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ef97b8df011141c9b0f6caf23b29379f87dd13183c978a30a3c546d2c47314cb"}, - {file = "kiwisolver-1.4.7-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3ab58c12a2cd0fc769089e6d38466c46d7f76aced0a1f54c77652446733d2d02"}, - {file = "kiwisolver-1.4.7-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:803b8e1459341c1bb56d1c5c010406d5edec8a0713a0945851290a7930679b51"}, - {file = "kiwisolver-1.4.7-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f9a9e8a507420fe35992ee9ecb302dab68550dedc0da9e2880dd88071c5fb052"}, - {file = "kiwisolver-1.4.7-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:18077b53dc3bb490e330669a99920c5e6a496889ae8c63b58fbc57c3d7f33a18"}, - {file = "kiwisolver-1.4.7-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6af936f79086a89b3680a280c47ea90b4df7047b5bdf3aa5c524bbedddb9e545"}, - {file = "kiwisolver-1.4.7-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:3abc5b19d24af4b77d1598a585b8a719beb8569a71568b66f4ebe1fb0449460b"}, - {file = "kiwisolver-1.4.7-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:933d4de052939d90afbe6e9d5273ae05fb836cc86c15b686edd4b3560cc0ee36"}, - {file = "kiwisolver-1.4.7-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:65e720d2ab2b53f1f72fb5da5fb477455905ce2c88aaa671ff0a447c2c80e8e3"}, - {file = "kiwisolver-1.4.7-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3bf1ed55088f214ba6427484c59553123fdd9b218a42bbc8c6496d6754b1e523"}, - {file = "kiwisolver-1.4.7-cp311-cp311-win32.whl", hash = "sha256:4c00336b9dd5ad96d0a558fd18a8b6f711b7449acce4c157e7343ba92dd0cf3d"}, - {file = "kiwisolver-1.4.7-cp311-cp311-win_amd64.whl", hash = "sha256:929e294c1ac1e9f615c62a4e4313ca1823ba37326c164ec720a803287c4c499b"}, - {file = "kiwisolver-1.4.7-cp311-cp311-win_arm64.whl", hash = "sha256:e33e8fbd440c917106b237ef1a2f1449dfbb9b6f6e1ce17c94cd6a1e0d438376"}, - {file = "kiwisolver-1.4.7-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:5360cc32706dab3931f738d3079652d20982511f7c0ac5711483e6eab08efff2"}, - {file = "kiwisolver-1.4.7-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:942216596dc64ddb25adb215c3c783215b23626f8d84e8eff8d6d45c3f29f75a"}, - {file = "kiwisolver-1.4.7-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:48b571ecd8bae15702e4f22d3ff6a0f13e54d3d00cd25216d5e7f658242065ee"}, - {file = "kiwisolver-1.4.7-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ad42ba922c67c5f219097b28fae965e10045ddf145d2928bfac2eb2e17673640"}, - {file = "kiwisolver-1.4.7-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:612a10bdae23404a72941a0fc8fa2660c6ea1217c4ce0dbcab8a8f6543ea9e7f"}, - {file = "kiwisolver-1.4.7-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9e838bba3a3bac0fe06d849d29772eb1afb9745a59710762e4ba3f4cb8424483"}, - {file = "kiwisolver-1.4.7-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:22f499f6157236c19f4bbbd472fa55b063db77a16cd74d49afe28992dff8c258"}, - {file = "kiwisolver-1.4.7-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:693902d433cf585133699972b6d7c42a8b9f8f826ebcaf0132ff55200afc599e"}, - {file = "kiwisolver-1.4.7-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:4e77f2126c3e0b0d055f44513ed349038ac180371ed9b52fe96a32aa071a5107"}, - {file = "kiwisolver-1.4.7-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:657a05857bda581c3656bfc3b20e353c232e9193eb167766ad2dc58b56504948"}, - {file = "kiwisolver-1.4.7-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:4bfa75a048c056a411f9705856abfc872558e33c055d80af6a380e3658766038"}, - {file = "kiwisolver-1.4.7-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:34ea1de54beef1c104422d210c47c7d2a4999bdecf42c7b5718fbe59a4cac383"}, - {file = "kiwisolver-1.4.7-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:90da3b5f694b85231cf93586dad5e90e2d71b9428f9aad96952c99055582f520"}, - {file = "kiwisolver-1.4.7-cp312-cp312-win32.whl", hash = "sha256:18e0cca3e008e17fe9b164b55735a325140a5a35faad8de92dd80265cd5eb80b"}, - {file = "kiwisolver-1.4.7-cp312-cp312-win_amd64.whl", hash = "sha256:58cb20602b18f86f83a5c87d3ee1c766a79c0d452f8def86d925e6c60fbf7bfb"}, - {file = "kiwisolver-1.4.7-cp312-cp312-win_arm64.whl", hash = "sha256:f5a8b53bdc0b3961f8b6125e198617c40aeed638b387913bf1ce78afb1b0be2a"}, - {file = "kiwisolver-1.4.7-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:2e6039dcbe79a8e0f044f1c39db1986a1b8071051efba3ee4d74f5b365f5226e"}, - {file = "kiwisolver-1.4.7-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a1ecf0ac1c518487d9d23b1cd7139a6a65bc460cd101ab01f1be82ecf09794b6"}, - {file = "kiwisolver-1.4.7-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7ab9ccab2b5bd5702ab0803676a580fffa2aa178c2badc5557a84cc943fcf750"}, - {file = "kiwisolver-1.4.7-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f816dd2277f8d63d79f9c8473a79fe54047bc0467754962840782c575522224d"}, - {file = "kiwisolver-1.4.7-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cf8bcc23ceb5a1b624572a1623b9f79d2c3b337c8c455405ef231933a10da379"}, - {file = "kiwisolver-1.4.7-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dea0bf229319828467d7fca8c7c189780aa9ff679c94539eed7532ebe33ed37c"}, - {file = "kiwisolver-1.4.7-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c06a4c7cf15ec739ce0e5971b26c93638730090add60e183530d70848ebdd34"}, - {file = "kiwisolver-1.4.7-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:913983ad2deb14e66d83c28b632fd35ba2b825031f2fa4ca29675e665dfecbe1"}, - {file = "kiwisolver-1.4.7-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5337ec7809bcd0f424c6b705ecf97941c46279cf5ed92311782c7c9c2026f07f"}, - {file = "kiwisolver-1.4.7-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:4c26ed10c4f6fa6ddb329a5120ba3b6db349ca192ae211e882970bfc9d91420b"}, - {file = "kiwisolver-1.4.7-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c619b101e6de2222c1fcb0531e1b17bbffbe54294bfba43ea0d411d428618c27"}, - {file = "kiwisolver-1.4.7-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:073a36c8273647592ea332e816e75ef8da5c303236ec0167196793eb1e34657a"}, - {file = "kiwisolver-1.4.7-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:3ce6b2b0231bda412463e152fc18335ba32faf4e8c23a754ad50ffa70e4091ee"}, - {file = "kiwisolver-1.4.7-cp313-cp313-win32.whl", hash = "sha256:f4c9aee212bc89d4e13f58be11a56cc8036cabad119259d12ace14b34476fd07"}, - {file = "kiwisolver-1.4.7-cp313-cp313-win_amd64.whl", hash = "sha256:8a3ec5aa8e38fc4c8af308917ce12c536f1c88452ce554027e55b22cbbfbff76"}, - {file = "kiwisolver-1.4.7-cp313-cp313-win_arm64.whl", hash = "sha256:76c8094ac20ec259471ac53e774623eb62e6e1f56cd8690c67ce6ce4fcb05650"}, - {file = "kiwisolver-1.4.7-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5d5abf8f8ec1f4e22882273c423e16cae834c36856cac348cfbfa68e01c40f3a"}, - {file = "kiwisolver-1.4.7-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:aeb3531b196ef6f11776c21674dba836aeea9d5bd1cf630f869e3d90b16cfade"}, - {file = "kiwisolver-1.4.7-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b7d755065e4e866a8086c9bdada157133ff466476a2ad7861828e17b6026e22c"}, - {file = "kiwisolver-1.4.7-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:08471d4d86cbaec61f86b217dd938a83d85e03785f51121e791a6e6689a3be95"}, - {file = "kiwisolver-1.4.7-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7bbfcb7165ce3d54a3dfbe731e470f65739c4c1f85bb1018ee912bae139e263b"}, - {file = "kiwisolver-1.4.7-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5d34eb8494bea691a1a450141ebb5385e4b69d38bb8403b5146ad279f4b30fa3"}, - {file = "kiwisolver-1.4.7-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9242795d174daa40105c1d86aba618e8eab7bf96ba8c3ee614da8302a9f95503"}, - {file = "kiwisolver-1.4.7-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:a0f64a48bb81af7450e641e3fe0b0394d7381e342805479178b3d335d60ca7cf"}, - {file = "kiwisolver-1.4.7-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:8e045731a5416357638d1700927529e2b8ab304811671f665b225f8bf8d8f933"}, - {file = "kiwisolver-1.4.7-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:4322872d5772cae7369f8351da1edf255a604ea7087fe295411397d0cfd9655e"}, - {file = "kiwisolver-1.4.7-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:e1631290ee9271dffe3062d2634c3ecac02c83890ada077d225e081aca8aab89"}, - {file = "kiwisolver-1.4.7-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:edcfc407e4eb17e037bca59be0e85a2031a2ac87e4fed26d3e9df88b4165f92d"}, - {file = "kiwisolver-1.4.7-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:4d05d81ecb47d11e7f8932bd8b61b720bf0b41199358f3f5e36d38e28f0532c5"}, - {file = "kiwisolver-1.4.7-cp38-cp38-win32.whl", hash = "sha256:b38ac83d5f04b15e515fd86f312479d950d05ce2368d5413d46c088dda7de90a"}, - {file = "kiwisolver-1.4.7-cp38-cp38-win_amd64.whl", hash = "sha256:d83db7cde68459fc803052a55ace60bea2bae361fc3b7a6d5da07e11954e4b09"}, - {file = "kiwisolver-1.4.7-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:3f9362ecfca44c863569d3d3c033dbe8ba452ff8eed6f6b5806382741a1334bd"}, - {file = "kiwisolver-1.4.7-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:e8df2eb9b2bac43ef8b082e06f750350fbbaf2887534a5be97f6cf07b19d9583"}, - {file = "kiwisolver-1.4.7-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f32d6edbc638cde7652bd690c3e728b25332acbadd7cad670cc4a02558d9c417"}, - {file = "kiwisolver-1.4.7-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:e2e6c39bd7b9372b0be21456caab138e8e69cc0fc1190a9dfa92bd45a1e6e904"}, - {file = "kiwisolver-1.4.7-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:dda56c24d869b1193fcc763f1284b9126550eaf84b88bbc7256e15028f19188a"}, - {file = "kiwisolver-1.4.7-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:79849239c39b5e1fd906556c474d9b0439ea6792b637511f3fe3a41158d89ca8"}, - {file = "kiwisolver-1.4.7-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5e3bc157fed2a4c02ec468de4ecd12a6e22818d4f09cde2c31ee3226ffbefab2"}, - {file = "kiwisolver-1.4.7-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3da53da805b71e41053dc670f9a820d1157aae77b6b944e08024d17bcd51ef88"}, - {file = "kiwisolver-1.4.7-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:8705f17dfeb43139a692298cb6637ee2e59c0194538153e83e9ee0c75c2eddde"}, - {file = "kiwisolver-1.4.7-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:82a5c2f4b87c26bb1a0ef3d16b5c4753434633b83d365cc0ddf2770c93829e3c"}, - {file = "kiwisolver-1.4.7-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:ce8be0466f4c0d585cdb6c1e2ed07232221df101a4c6f28821d2aa754ca2d9e2"}, - {file = "kiwisolver-1.4.7-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:409afdfe1e2e90e6ee7fc896f3df9a7fec8e793e58bfa0d052c8a82f99c37abb"}, - {file = "kiwisolver-1.4.7-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:5b9c3f4ee0b9a439d2415012bd1b1cc2df59e4d6a9939f4d669241d30b414327"}, - {file = "kiwisolver-1.4.7-cp39-cp39-win32.whl", hash = "sha256:a79ae34384df2b615eefca647a2873842ac3b596418032bef9a7283675962644"}, - {file = "kiwisolver-1.4.7-cp39-cp39-win_amd64.whl", hash = "sha256:cf0438b42121a66a3a667de17e779330fc0f20b0d97d59d2f2121e182b0505e4"}, - {file = "kiwisolver-1.4.7-cp39-cp39-win_arm64.whl", hash = "sha256:764202cc7e70f767dab49e8df52c7455e8de0df5d858fa801a11aa0d882ccf3f"}, - {file = "kiwisolver-1.4.7-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:94252291e3fe68001b1dd747b4c0b3be12582839b95ad4d1b641924d68fd4643"}, - {file = "kiwisolver-1.4.7-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:5b7dfa3b546da08a9f622bb6becdb14b3e24aaa30adba66749d38f3cc7ea9706"}, - {file = "kiwisolver-1.4.7-pp310-pypy310_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bd3de6481f4ed8b734da5df134cd5a6a64fe32124fe83dde1e5b5f29fe30b1e6"}, - {file = "kiwisolver-1.4.7-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a91b5f9f1205845d488c928e8570dcb62b893372f63b8b6e98b863ebd2368ff2"}, - {file = "kiwisolver-1.4.7-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:40fa14dbd66b8b8f470d5fc79c089a66185619d31645f9b0773b88b19f7223c4"}, - {file = "kiwisolver-1.4.7-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:eb542fe7933aa09d8d8f9d9097ef37532a7df6497819d16efe4359890a2f417a"}, - {file = "kiwisolver-1.4.7-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:bfa1acfa0c54932d5607e19a2c24646fb4c1ae2694437789129cf099789a3b00"}, - {file = "kiwisolver-1.4.7-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:eee3ea935c3d227d49b4eb85660ff631556841f6e567f0f7bda972df6c2c9935"}, - {file = "kiwisolver-1.4.7-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:f3160309af4396e0ed04db259c3ccbfdc3621b5559b5453075e5de555e1f3a1b"}, - {file = "kiwisolver-1.4.7-pp38-pypy38_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:a17f6a29cf8935e587cc8a4dbfc8368c55edc645283db0ce9801016f83526c2d"}, - {file = "kiwisolver-1.4.7-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:10849fb2c1ecbfae45a693c070e0320a91b35dd4bcf58172c023b994283a124d"}, - {file = "kiwisolver-1.4.7-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:ac542bf38a8a4be2dc6b15248d36315ccc65f0743f7b1a76688ffb6b5129a5c2"}, - {file = "kiwisolver-1.4.7-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:8b01aac285f91ca889c800042c35ad3b239e704b150cfd3382adfc9dcc780e39"}, - {file = "kiwisolver-1.4.7-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:48be928f59a1f5c8207154f935334d374e79f2b5d212826307d072595ad76a2e"}, - {file = "kiwisolver-1.4.7-pp39-pypy39_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f37cfe618a117e50d8c240555331160d73d0411422b59b5ee217843d7b693608"}, - {file = "kiwisolver-1.4.7-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:599b5c873c63a1f6ed7eead644a8a380cfbdf5db91dcb6f85707aaab213b1674"}, - {file = "kiwisolver-1.4.7-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:801fa7802e5cfabe3ab0c81a34c323a319b097dfb5004be950482d882f3d7225"}, - {file = "kiwisolver-1.4.7-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:0c6c43471bc764fad4bc99c5c2d6d16a676b1abf844ca7c8702bdae92df01ee0"}, - {file = "kiwisolver-1.4.7.tar.gz", hash = "sha256:9893ff81bd7107f7b685d3017cc6583daadb4fc26e4a888350df530e41980a60"}, -] - -[[package]] -name = "langchain" -version = "0.2.17" -description = "Building applications with LLMs through composability" -optional = false -python-versions = "<4.0,>=3.8.1" -files = [ - {file = "langchain-0.2.17-py3-none-any.whl", hash = "sha256:a97a33e775f8de074370aecab95db148b879c794695d9e443c95457dce5eb525"}, - {file = "langchain-0.2.17.tar.gz", hash = "sha256:5a99ce94aae05925851777dba45cbf2c475565d1e91cbe7d82c5e329d514627e"}, -] - -[package.dependencies] -aiohttp = ">=3.8.3,<4.0.0" -langchain-core = ">=0.2.43,<0.3.0" -langchain-text-splitters = ">=0.2.0,<0.3.0" -langsmith = ">=0.1.17,<0.2.0" -numpy = {version = ">=1,<2", markers = "python_version < \"3.12\""} -pydantic = ">=1,<3" -PyYAML = ">=5.3" -requests = ">=2,<3" -SQLAlchemy = ">=1.4,<3" -tenacity = ">=8.1.0,<8.4.0 || >8.4.0,<9.0.0" - -[[package]] -name = "langchain-community" -version = "0.2.19" -description = "Community contributed LangChain integrations." -optional = false -python-versions = "<4.0,>=3.8.1" -files = [ - {file = "langchain_community-0.2.19-py3-none-any.whl", hash = "sha256:651d761f2d37d63f89de75d65858f6c7f6ea99c455622e9c13ca041622dad0c5"}, - {file = "langchain_community-0.2.19.tar.gz", hash = "sha256:74f8db6992d03668c3d82e0d896845c413d167dad3b8e349fb2a9a57fd2d1396"}, -] - -[package.dependencies] -aiohttp = ">=3.8.3,<4.0.0" -dataclasses-json = ">=0.5.7,<0.7" -langchain = ">=0.2.17,<0.3.0" -langchain-core = ">=0.2.43,<0.3.0" -langsmith = ">=0.1.112,<0.2.0" -numpy = {version = ">=1,<2", markers = "python_version < \"3.12\""} -PyYAML = ">=5.3" -requests = ">=2,<3" -SQLAlchemy = ">=1.4,<3" -tenacity = ">=8.1.0,<8.4.0 || >8.4.0,<9.0.0" - -[[package]] -name = "langchain-core" -version = "0.2.43" -description = "Building applications with LLMs through composability" -optional = false -python-versions = "<4.0,>=3.8.1" -files = [ - {file = "langchain_core-0.2.43-py3-none-any.whl", hash = "sha256:619601235113298ebf8252a349754b7c28d3cf7166c7c922da24944b78a9363a"}, - {file = "langchain_core-0.2.43.tar.gz", hash = "sha256:42c2ef6adedb911f4254068b6adc9eb4c4075f6c8cb3d83590d3539a815695f5"}, -] - -[package.dependencies] -jsonpatch = ">=1.33,<2.0" -langsmith = ">=0.1.112,<0.2.0" -packaging = ">=23.2,<25" -pydantic = {version = ">=1,<3", markers = "python_full_version < \"3.12.4\""} -PyYAML = ">=5.3" -tenacity = ">=8.1.0,<8.4.0 || >8.4.0,<9.0.0" -typing-extensions = ">=4.7" - -[[package]] -name = "langchain-openai" -version = "0.1.25" -description = "An integration package connecting OpenAI and LangChain" -optional = false -python-versions = "<4.0,>=3.8.1" -files = [ - {file = "langchain_openai-0.1.25-py3-none-any.whl", hash = "sha256:f0b34a233d0d9cb8fce6006c903e57085c493c4f0e32862b99063b96eaedb109"}, - {file = "langchain_openai-0.1.25.tar.gz", hash = "sha256:eb116f744f820247a72f54313fb7c01524fba0927120d4e899e5e4ab41ad3928"}, -] - -[package.dependencies] -langchain-core = ">=0.2.40,<0.3.0" -openai = ">=1.40.0,<2.0.0" -tiktoken = ">=0.7,<1" - -[[package]] -name = "langchain-text-splitters" -version = "0.2.4" -description = "LangChain text splitting utilities" -optional = false -python-versions = "<4.0,>=3.8.1" +python-versions = ">=3.10" files = [ - {file = "langchain_text_splitters-0.2.4-py3-none-any.whl", hash = "sha256:2702dee5b7cbdd595ccbe43b8d38d01a34aa8583f4d6a5a68ad2305ae3e7b645"}, - {file = "langchain_text_splitters-0.2.4.tar.gz", hash = "sha256:f7daa7a3b0aa8309ce248e2e2b6fc8115be01118d336c7f7f7dfacda0e89bf29"}, + {file = "kiwisolver-1.4.8-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:88c6f252f6816a73b1f8c904f7bbe02fd67c09a69f7cb8a0eecdbf5ce78e63db"}, + {file = "kiwisolver-1.4.8-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c72941acb7b67138f35b879bbe85be0f6c6a70cab78fe3ef6db9c024d9223e5b"}, + {file = "kiwisolver-1.4.8-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ce2cf1e5688edcb727fdf7cd1bbd0b6416758996826a8be1d958f91880d0809d"}, + {file = "kiwisolver-1.4.8-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:c8bf637892dc6e6aad2bc6d4d69d08764166e5e3f69d469e55427b6ac001b19d"}, + {file = "kiwisolver-1.4.8-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:034d2c891f76bd3edbdb3ea11140d8510dca675443da7304205a2eaa45d8334c"}, + {file = "kiwisolver-1.4.8-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d47b28d1dfe0793d5e96bce90835e17edf9a499b53969b03c6c47ea5985844c3"}, + {file = "kiwisolver-1.4.8-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:eb158fe28ca0c29f2260cca8c43005329ad58452c36f0edf298204de32a9a3ed"}, + {file = "kiwisolver-1.4.8-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d5536185fce131780ebd809f8e623bf4030ce1b161353166c49a3c74c287897f"}, + {file = "kiwisolver-1.4.8-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:369b75d40abedc1da2c1f4de13f3482cb99e3237b38726710f4a793432b1c5ff"}, + {file = "kiwisolver-1.4.8-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:641f2ddf9358c80faa22e22eb4c9f54bd3f0e442e038728f500e3b978d00aa7d"}, + {file = "kiwisolver-1.4.8-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:d561d2d8883e0819445cfe58d7ddd673e4015c3c57261d7bdcd3710d0d14005c"}, + {file = "kiwisolver-1.4.8-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:1732e065704b47c9afca7ffa272f845300a4eb959276bf6970dc07265e73b605"}, + {file = "kiwisolver-1.4.8-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:bcb1ebc3547619c3b58a39e2448af089ea2ef44b37988caf432447374941574e"}, + {file = "kiwisolver-1.4.8-cp310-cp310-win_amd64.whl", hash = "sha256:89c107041f7b27844179ea9c85d6da275aa55ecf28413e87624d033cf1f6b751"}, + {file = "kiwisolver-1.4.8-cp310-cp310-win_arm64.whl", hash = "sha256:b5773efa2be9eb9fcf5415ea3ab70fc785d598729fd6057bea38d539ead28271"}, + {file = "kiwisolver-1.4.8-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a4d3601908c560bdf880f07d94f31d734afd1bb71e96585cace0e38ef44c6d84"}, + {file = "kiwisolver-1.4.8-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:856b269c4d28a5c0d5e6c1955ec36ebfd1651ac00e1ce0afa3e28da95293b561"}, + {file = "kiwisolver-1.4.8-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c2b9a96e0f326205af81a15718a9073328df1173a2619a68553decb7097fd5d7"}, + {file = "kiwisolver-1.4.8-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c5020c83e8553f770cb3b5fc13faac40f17e0b205bd237aebd21d53d733adb03"}, + {file = "kiwisolver-1.4.8-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dace81d28c787956bfbfbbfd72fdcef014f37d9b48830829e488fdb32b49d954"}, + {file = "kiwisolver-1.4.8-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:11e1022b524bd48ae56c9b4f9296bce77e15a2e42a502cceba602f804b32bb79"}, + {file = "kiwisolver-1.4.8-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3b9b4d2892fefc886f30301cdd80debd8bb01ecdf165a449eb6e78f79f0fabd6"}, + {file = "kiwisolver-1.4.8-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a96c0e790ee875d65e340ab383700e2b4891677b7fcd30a699146f9384a2bb0"}, + {file = "kiwisolver-1.4.8-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:23454ff084b07ac54ca8be535f4174170c1094a4cff78fbae4f73a4bcc0d4dab"}, + {file = "kiwisolver-1.4.8-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:87b287251ad6488e95b4f0b4a79a6d04d3ea35fde6340eb38fbd1ca9cd35bbbc"}, + {file = "kiwisolver-1.4.8-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:b21dbe165081142b1232a240fc6383fd32cdd877ca6cc89eab93e5f5883e1c25"}, + {file = "kiwisolver-1.4.8-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:768cade2c2df13db52475bd28d3a3fac8c9eff04b0e9e2fda0f3760f20b3f7fc"}, + {file = "kiwisolver-1.4.8-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:d47cfb2650f0e103d4bf68b0b5804c68da97272c84bb12850d877a95c056bd67"}, + {file = "kiwisolver-1.4.8-cp311-cp311-win_amd64.whl", hash = "sha256:ed33ca2002a779a2e20eeb06aea7721b6e47f2d4b8a8ece979d8ba9e2a167e34"}, + {file = "kiwisolver-1.4.8-cp311-cp311-win_arm64.whl", hash = "sha256:16523b40aab60426ffdebe33ac374457cf62863e330a90a0383639ce14bf44b2"}, + {file = "kiwisolver-1.4.8-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:d6af5e8815fd02997cb6ad9bbed0ee1e60014438ee1a5c2444c96f87b8843502"}, + {file = "kiwisolver-1.4.8-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:bade438f86e21d91e0cf5dd7c0ed00cda0f77c8c1616bd83f9fc157fa6760d31"}, + {file = "kiwisolver-1.4.8-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b83dc6769ddbc57613280118fb4ce3cd08899cc3369f7d0e0fab518a7cf37fdb"}, + {file = "kiwisolver-1.4.8-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:111793b232842991be367ed828076b03d96202c19221b5ebab421ce8bcad016f"}, + {file = "kiwisolver-1.4.8-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:257af1622860e51b1a9d0ce387bf5c2c4f36a90594cb9514f55b074bcc787cfc"}, + {file = "kiwisolver-1.4.8-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:69b5637c3f316cab1ec1c9a12b8c5f4750a4c4b71af9157645bf32830e39c03a"}, + {file = "kiwisolver-1.4.8-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:782bb86f245ec18009890e7cb8d13a5ef54dcf2ebe18ed65f795e635a96a1c6a"}, + {file = "kiwisolver-1.4.8-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cc978a80a0db3a66d25767b03688f1147a69e6237175c0f4ffffaaedf744055a"}, + {file = "kiwisolver-1.4.8-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:36dbbfd34838500a31f52c9786990d00150860e46cd5041386f217101350f0d3"}, + {file = "kiwisolver-1.4.8-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:eaa973f1e05131de5ff3569bbba7f5fd07ea0595d3870ed4a526d486fe57fa1b"}, + {file = "kiwisolver-1.4.8-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:a66f60f8d0c87ab7f59b6fb80e642ebb29fec354a4dfad687ca4092ae69d04f4"}, + {file = "kiwisolver-1.4.8-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:858416b7fb777a53f0c59ca08190ce24e9abbd3cffa18886a5781b8e3e26f65d"}, + {file = "kiwisolver-1.4.8-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:085940635c62697391baafaaeabdf3dd7a6c3643577dde337f4d66eba021b2b8"}, + {file = "kiwisolver-1.4.8-cp312-cp312-win_amd64.whl", hash = "sha256:01c3d31902c7db5fb6182832713d3b4122ad9317c2c5877d0539227d96bb2e50"}, + {file = "kiwisolver-1.4.8-cp312-cp312-win_arm64.whl", hash = "sha256:a3c44cb68861de93f0c4a8175fbaa691f0aa22550c331fefef02b618a9dcb476"}, + {file = "kiwisolver-1.4.8-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:1c8ceb754339793c24aee1c9fb2485b5b1f5bb1c2c214ff13368431e51fc9a09"}, + {file = "kiwisolver-1.4.8-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:54a62808ac74b5e55a04a408cda6156f986cefbcf0ada13572696b507cc92fa1"}, + {file = "kiwisolver-1.4.8-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:68269e60ee4929893aad82666821aaacbd455284124817af45c11e50a4b42e3c"}, + {file = "kiwisolver-1.4.8-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:34d142fba9c464bc3bbfeff15c96eab0e7310343d6aefb62a79d51421fcc5f1b"}, + {file = "kiwisolver-1.4.8-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3ddc373e0eef45b59197de815b1b28ef89ae3955e7722cc9710fb91cd77b7f47"}, + {file = "kiwisolver-1.4.8-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:77e6f57a20b9bd4e1e2cedda4d0b986ebd0216236f0106e55c28aea3d3d69b16"}, + {file = "kiwisolver-1.4.8-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:08e77738ed7538f036cd1170cbed942ef749137b1311fa2bbe2a7fda2f6bf3cc"}, + {file = "kiwisolver-1.4.8-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a5ce1e481a74b44dd5e92ff03ea0cb371ae7a0268318e202be06c8f04f4f1246"}, + {file = "kiwisolver-1.4.8-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:fc2ace710ba7c1dfd1a3b42530b62b9ceed115f19a1656adefce7b1782a37794"}, + {file = "kiwisolver-1.4.8-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:3452046c37c7692bd52b0e752b87954ef86ee2224e624ef7ce6cb21e8c41cc1b"}, + {file = "kiwisolver-1.4.8-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:7e9a60b50fe8b2ec6f448fe8d81b07e40141bfced7f896309df271a0b92f80f3"}, + {file = "kiwisolver-1.4.8-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:918139571133f366e8362fa4a297aeba86c7816b7ecf0bc79168080e2bd79957"}, + {file = "kiwisolver-1.4.8-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e063ef9f89885a1d68dd8b2e18f5ead48653176d10a0e324e3b0030e3a69adeb"}, + {file = "kiwisolver-1.4.8-cp313-cp313-win_amd64.whl", hash = "sha256:a17b7c4f5b2c51bb68ed379defd608a03954a1845dfed7cc0117f1cc8a9b7fd2"}, + {file = "kiwisolver-1.4.8-cp313-cp313-win_arm64.whl", hash = "sha256:3cd3bc628b25f74aedc6d374d5babf0166a92ff1317f46267f12d2ed54bc1d30"}, + {file = "kiwisolver-1.4.8-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:370fd2df41660ed4e26b8c9d6bbcad668fbe2560462cba151a721d49e5b6628c"}, + {file = "kiwisolver-1.4.8-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:84a2f830d42707de1d191b9490ac186bf7997a9495d4e9072210a1296345f7dc"}, + {file = "kiwisolver-1.4.8-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:7a3ad337add5148cf51ce0b55642dc551c0b9d6248458a757f98796ca7348712"}, + {file = "kiwisolver-1.4.8-cp313-cp313t-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7506488470f41169b86d8c9aeff587293f530a23a23a49d6bc64dab66bedc71e"}, + {file = "kiwisolver-1.4.8-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2f0121b07b356a22fb0414cec4666bbe36fd6d0d759db3d37228f496ed67c880"}, + {file = "kiwisolver-1.4.8-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d6d6bd87df62c27d4185de7c511c6248040afae67028a8a22012b010bc7ad062"}, + {file = "kiwisolver-1.4.8-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:291331973c64bb9cce50bbe871fb2e675c4331dab4f31abe89f175ad7679a4d7"}, + {file = "kiwisolver-1.4.8-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:893f5525bb92d3d735878ec00f781b2de998333659507d29ea4466208df37bed"}, + {file = "kiwisolver-1.4.8-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:b47a465040146981dc9db8647981b8cb96366fbc8d452b031e4f8fdffec3f26d"}, + {file = "kiwisolver-1.4.8-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:99cea8b9dd34ff80c521aef46a1dddb0dcc0283cf18bde6d756f1e6f31772165"}, + {file = "kiwisolver-1.4.8-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:151dffc4865e5fe6dafce5480fab84f950d14566c480c08a53c663a0020504b6"}, + {file = "kiwisolver-1.4.8-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:577facaa411c10421314598b50413aa1ebcf5126f704f1e5d72d7e4e9f020d90"}, + {file = "kiwisolver-1.4.8-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:be4816dc51c8a471749d664161b434912eee82f2ea66bd7628bd14583a833e85"}, + {file = "kiwisolver-1.4.8-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:e7a019419b7b510f0f7c9dceff8c5eae2392037eae483a7f9162625233802b0a"}, + {file = "kiwisolver-1.4.8-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:286b18e86682fd2217a48fc6be6b0f20c1d0ed10958d8dc53453ad58d7be0bf8"}, + {file = "kiwisolver-1.4.8-pp310-pypy310_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4191ee8dfd0be1c3666ccbac178c5a05d5f8d689bbe3fc92f3c4abec817f8fe0"}, + {file = "kiwisolver-1.4.8-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7cd2785b9391f2873ad46088ed7599a6a71e762e1ea33e87514b1a441ed1da1c"}, + {file = "kiwisolver-1.4.8-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c07b29089b7ba090b6f1a669f1411f27221c3662b3a1b7010e67b59bb5a6f10b"}, + {file = "kiwisolver-1.4.8-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:65ea09a5a3faadd59c2ce96dc7bf0f364986a315949dc6374f04396b0d60e09b"}, + {file = "kiwisolver-1.4.8.tar.gz", hash = "sha256:23d5f023bdc8c7e54eb65f03ca5d5bb25b601eac4d7f1a042888a1f45237987e"}, ] -[package.dependencies] -langchain-core = ">=0.2.38,<0.3.0" - [[package]] name = "langdetect" version = "1.0.9" @@ -2496,27 +2114,6 @@ files = [ [package.dependencies] six = "*" -[[package]] -name = "langsmith" -version = "0.1.147" -description = "Client library to connect to the LangSmith LLM Tracing and Evaluation Platform." -optional = false -python-versions = "<4.0,>=3.8.1" -files = [ - {file = "langsmith-0.1.147-py3-none-any.whl", hash = "sha256:7166fc23b965ccf839d64945a78e9f1157757add228b086141eb03a60d699a15"}, - {file = "langsmith-0.1.147.tar.gz", hash = "sha256:2e933220318a4e73034657103b3b1a3a6109cc5db3566a7e8e03be8d6d7def7a"}, -] - -[package.dependencies] -httpx = ">=0.23.0,<1" -orjson = {version = ">=3.9.14,<4.0.0", markers = "platform_python_implementation != \"PyPy\""} -pydantic = {version = ">=1,<3", markers = "python_full_version < \"3.12.4\""} -requests = ">=2,<3" -requests-toolbelt = ">=1.0.0,<2.0.0" - -[package.extras] -langsmith-pyo3 = ["langsmith-pyo3 (>=0.1.0rc2,<0.2.0)"] - [[package]] name = "libipld" version = "3.0.0" @@ -3054,6 +2651,17 @@ files = [ {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, ] +[[package]] +name = "ndjson" +version = "0.3.1" +description = "JsonDecoder for ndjson" +optional = false +python-versions = "*" +files = [ + {file = "ndjson-0.3.1-py2.py3-none-any.whl", hash = "sha256:839c22275e6baa3040077b83c005ac24199b94973309a8a1809be962c753a410"}, + {file = "ndjson-0.3.1.tar.gz", hash = "sha256:bf9746cb6bb1cb53d172cda7f154c07c786d665ff28341e4e689b796b229e5d6"}, +] + [[package]] name = "nest-asyncio" version = "1.6.0" @@ -3065,6 +2673,47 @@ files = [ {file = "nest_asyncio-1.6.0.tar.gz", hash = "sha256:6f172d5449aca15afd6c646851f4e31e02c598d553a667e38cafa997cfec55fe"}, ] +[[package]] +name = "newrelic" +version = "10.4.0" +description = "New Relic Python Agent" +optional = false +python-versions = ">=3.7" +files = [ + {file = "newrelic-10.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a8336e0c569ad28ad33427c99bf4a5cb31ec5df8d95ac66c6065023f0b108a3a"}, + {file = "newrelic-10.4.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e5c32c767b64369a8b816629688e62efc58f5ff81b5f4d97f7cf8f3f23d4deb"}, + {file = "newrelic-10.4.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a6658cfe3924c646c1b78a70f60eb890842d8fc31f752e32a450022d397672b8"}, + {file = "newrelic-10.4.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:4ecf94d7c9103c304d6e27afdf701fd62fcd4320d9ab8d94bcaf4c77cf4576a9"}, + {file = "newrelic-10.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40a3359672e99c64226b33599daa0d210128a1c7540e26e86400e5710741bf17"}, + {file = "newrelic-10.4.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:12572fc2f487dbdb6d2ebec6b1edbe3c3955af7638acf0bda6eff93072d45230"}, + {file = "newrelic-10.4.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:844a38c3ddeb3d2e2d6e568ab8a7f38e12ceaefd77261c66d2c4c9a20c2596ae"}, + {file = "newrelic-10.4.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:64da7c1155066d115e63523edfd4b9f39361b4c19a46fdde4b710b00342fb31d"}, + {file = "newrelic-10.4.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c3d2ea64631663155e10f20a793076f271bf072e3564ecf6bd723f94358cb41c"}, + {file = "newrelic-10.4.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:22b7da78dc374cc5a72bceaf6c03bc26fda6fd398b242a8aa1221e75cfa540b0"}, + {file = "newrelic-10.4.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a4fdbdd3e6226b01baab71cc2d2406f9f83cd88ee12d72e48593a8f510e240ff"}, + {file = "newrelic-10.4.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:9df1bf1b1c0b294fce69bf0bbf87c03da544028f69ef6f1b597bf289a6b048e9"}, + {file = "newrelic-10.4.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:32fd7851d2c1c34632c4d6ae6bfe700703c99f1150ded0b16844257925d47861"}, + {file = "newrelic-10.4.0-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15a1b1331ff3950824a61dc356cc57acff2051e2f0ca00e30705ba5b10e8b9c9"}, + {file = "newrelic-10.4.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:8da411cbb14f675c99276e76c92557887f9f5e8044ab2e1e85d4f9826b4eca6b"}, + {file = "newrelic-10.4.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:c626fcde15412a905f2699f75d00d9c3af67e2b8f75f3ea1afdd6c0bb9a87d43"}, + {file = "newrelic-10.4.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9e50b0abfbfd2b6e84896341b6c6b60a1564a98fef64cc0c27b407dac95314e7"}, + {file = "newrelic-10.4.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:68b2f4fe10fed13cae551c41c90cb1686beee4daea0eb03559f552a58557a272"}, + {file = "newrelic-10.4.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:d69864f83039ffa17e6dab1d986d6a74be2401895b1ea9cb30453788b9372113"}, + {file = "newrelic-10.4.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:526ebed52279b7202f65b319574c0759620ab102331944211cadc71d5f205a8e"}, + {file = "newrelic-10.4.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cd36b7a4de9f7c49fde9849abc6fea293f70b09acad9a8573a514da5249b0f66"}, + {file = "newrelic-10.4.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b0297bfbef60ee57ce16e40192b3d317a8fb581f40a614a56672173db517117d"}, + {file = "newrelic-10.4.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:de25b50eba6e4b6cc9d0ccd980405ff2cceb4df52738a765dc55586a81c69d3a"}, + {file = "newrelic-10.4.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:12700b8560a29935ca3cb9868c6f977512d1d4d9f04a91ee16893c52273f0cc1"}, + {file = "newrelic-10.4.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b94ca109cfa54ab2c8270d9814923ee3c0abb9e9afd7da026e0b4869f8d93969"}, + {file = "newrelic-10.4.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e2a1b7b8b5414061bff996d2cef140ab26e9aa8d5e4e2cdce3ce8118adbf2c3e"}, + {file = "newrelic-10.4.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:6517683ba5c1170de100b0d9314dc3f542b5108acc535700018ba2fef26d3123"}, + {file = "newrelic-10.4.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:c8015ad51d9463c5c7c9977b99214013c276bf8bba3e3f97d5432308d93e37dd"}, + {file = "newrelic-10.4.0.tar.gz", hash = "sha256:4115eb9e5c97ad14e8cbae23dd5275dc162beb2b140c58ac4ba786c020f51aff"}, +] + +[package.extras] +infinite-tracing = ["grpcio", "protobuf"] + [[package]] name = "nltk" version = "3.9.1" @@ -3176,31 +2825,6 @@ files = [ [package.extras] tests = ["pytest", "pytest-cov"] -[[package]] -name = "openai" -version = "1.58.1" -description = "The official Python library for the openai API" -optional = false -python-versions = ">=3.8" -files = [ - {file = "openai-1.58.1-py3-none-any.whl", hash = "sha256:e2910b1170a6b7f88ef491ac3a42c387f08bd3db533411f7ee391d166571d63c"}, - {file = "openai-1.58.1.tar.gz", hash = "sha256:f5a035fd01e141fc743f4b0e02c41ca49be8fab0866d3b67f5f29b4f4d3c0973"}, -] - -[package.dependencies] -anyio = ">=3.5.0,<5" -distro = ">=1.7.0,<2" -httpx = ">=0.23.0,<1" -jiter = ">=0.4.0,<1" -pydantic = ">=1.9.0,<3" -sniffio = "*" -tqdm = ">4" -typing-extensions = ">=4.11,<5" - -[package.extras] -datalib = ["numpy (>=1)", "pandas (>=1.2.3)", "pandas-stubs (>=1.1.0.11)"] -realtime = ["websockets (>=13,<15)"] - [[package]] name = "openpyxl" version = "3.1.5" @@ -3226,90 +2850,6 @@ files = [ {file = "orderly_set-5.2.3.tar.gz", hash = "sha256:571ed97c5a5fca7ddeb6b2d26c19aca896b0ed91f334d9c109edd2f265fb3017"}, ] -[[package]] -name = "orjson" -version = "3.10.12" -description = "Fast, correct Python JSON library supporting dataclasses, datetimes, and numpy" -optional = false -python-versions = ">=3.8" -files = [ - {file = "orjson-3.10.12-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:ece01a7ec71d9940cc654c482907a6b65df27251255097629d0dea781f255c6d"}, - {file = "orjson-3.10.12-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c34ec9aebc04f11f4b978dd6caf697a2df2dd9b47d35aa4cc606cabcb9df69d7"}, - {file = "orjson-3.10.12-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fd6ec8658da3480939c79b9e9e27e0db31dffcd4ba69c334e98c9976ac29140e"}, - {file = "orjson-3.10.12-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f17e6baf4cf01534c9de8a16c0c611f3d94925d1701bf5f4aff17003677d8ced"}, - {file = "orjson-3.10.12-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6402ebb74a14ef96f94a868569f5dccf70d791de49feb73180eb3c6fda2ade56"}, - {file = "orjson-3.10.12-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0000758ae7c7853e0a4a6063f534c61656ebff644391e1f81698c1b2d2fc8cd2"}, - {file = "orjson-3.10.12-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:888442dcee99fd1e5bd37a4abb94930915ca6af4db50e23e746cdf4d1e63db13"}, - {file = "orjson-3.10.12-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:c1f7a3ce79246aa0e92f5458d86c54f257fb5dfdc14a192651ba7ec2c00f8a05"}, - {file = "orjson-3.10.12-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:802a3935f45605c66fb4a586488a38af63cb37aaad1c1d94c982c40dcc452e85"}, - {file = "orjson-3.10.12-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:1da1ef0113a2be19bb6c557fb0ec2d79c92ebd2fed4cfb1b26bab93f021fb885"}, - {file = "orjson-3.10.12-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7a3273e99f367f137d5b3fecb5e9f45bcdbfac2a8b2f32fbc72129bbd48789c2"}, - {file = "orjson-3.10.12-cp310-none-win32.whl", hash = "sha256:475661bf249fd7907d9b0a2a2421b4e684355a77ceef85b8352439a9163418c3"}, - {file = "orjson-3.10.12-cp310-none-win_amd64.whl", hash = "sha256:87251dc1fb2b9e5ab91ce65d8f4caf21910d99ba8fb24b49fd0c118b2362d509"}, - {file = "orjson-3.10.12-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:a734c62efa42e7df94926d70fe7d37621c783dea9f707a98cdea796964d4cf74"}, - {file = "orjson-3.10.12-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:750f8b27259d3409eda8350c2919a58b0cfcd2054ddc1bd317a643afc646ef23"}, - {file = "orjson-3.10.12-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bb52c22bfffe2857e7aa13b4622afd0dd9d16ea7cc65fd2bf318d3223b1b6252"}, - {file = "orjson-3.10.12-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:440d9a337ac8c199ff8251e100c62e9488924c92852362cd27af0e67308c16ef"}, - {file = "orjson-3.10.12-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a9e15c06491c69997dfa067369baab3bf094ecb74be9912bdc4339972323f252"}, - {file = "orjson-3.10.12-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:362d204ad4b0b8724cf370d0cd917bb2dc913c394030da748a3bb632445ce7c4"}, - {file = "orjson-3.10.12-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2b57cbb4031153db37b41622eac67329c7810e5f480fda4cfd30542186f006ae"}, - {file = "orjson-3.10.12-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:165c89b53ef03ce0d7c59ca5c82fa65fe13ddf52eeb22e859e58c237d4e33b9b"}, - {file = "orjson-3.10.12-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:5dee91b8dfd54557c1a1596eb90bcd47dbcd26b0baaed919e6861f076583e9da"}, - {file = "orjson-3.10.12-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:77a4e1cfb72de6f905bdff061172adfb3caf7a4578ebf481d8f0530879476c07"}, - {file = "orjson-3.10.12-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:038d42c7bc0606443459b8fe2d1f121db474c49067d8d14c6a075bbea8bf14dd"}, - {file = "orjson-3.10.12-cp311-none-win32.whl", hash = "sha256:03b553c02ab39bed249bedd4abe37b2118324d1674e639b33fab3d1dafdf4d79"}, - {file = "orjson-3.10.12-cp311-none-win_amd64.whl", hash = "sha256:8b8713b9e46a45b2af6b96f559bfb13b1e02006f4242c156cbadef27800a55a8"}, - {file = "orjson-3.10.12-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:53206d72eb656ca5ac7d3a7141e83c5bbd3ac30d5eccfe019409177a57634b0d"}, - {file = "orjson-3.10.12-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac8010afc2150d417ebda810e8df08dd3f544e0dd2acab5370cfa6bcc0662f8f"}, - {file = "orjson-3.10.12-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ed459b46012ae950dd2e17150e838ab08215421487371fa79d0eced8d1461d70"}, - {file = "orjson-3.10.12-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8dcb9673f108a93c1b52bfc51b0af422c2d08d4fc710ce9c839faad25020bb69"}, - {file = "orjson-3.10.12-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:22a51ae77680c5c4652ebc63a83d5255ac7d65582891d9424b566fb3b5375ee9"}, - {file = "orjson-3.10.12-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:910fdf2ac0637b9a77d1aad65f803bac414f0b06f720073438a7bd8906298192"}, - {file = "orjson-3.10.12-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:24ce85f7100160936bc2116c09d1a8492639418633119a2224114f67f63a4559"}, - {file = "orjson-3.10.12-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8a76ba5fc8dd9c913640292df27bff80a685bed3a3c990d59aa6ce24c352f8fc"}, - {file = "orjson-3.10.12-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:ff70ef093895fd53f4055ca75f93f047e088d1430888ca1229393a7c0521100f"}, - {file = "orjson-3.10.12-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:f4244b7018b5753ecd10a6d324ec1f347da130c953a9c88432c7fbc8875d13be"}, - {file = "orjson-3.10.12-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:16135ccca03445f37921fa4b585cff9a58aa8d81ebcb27622e69bfadd220b32c"}, - {file = "orjson-3.10.12-cp312-none-win32.whl", hash = "sha256:2d879c81172d583e34153d524fcba5d4adafbab8349a7b9f16ae511c2cee8708"}, - {file = "orjson-3.10.12-cp312-none-win_amd64.whl", hash = "sha256:fc23f691fa0f5c140576b8c365bc942d577d861a9ee1142e4db468e4e17094fb"}, - {file = "orjson-3.10.12-cp313-cp313-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:47962841b2a8aa9a258b377f5188db31ba49af47d4003a32f55d6f8b19006543"}, - {file = "orjson-3.10.12-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6334730e2532e77b6054e87ca84f3072bee308a45a452ea0bffbbbc40a67e296"}, - {file = "orjson-3.10.12-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:accfe93f42713c899fdac2747e8d0d5c659592df2792888c6c5f829472e4f85e"}, - {file = "orjson-3.10.12-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a7974c490c014c48810d1dede6c754c3cc46598da758c25ca3b4001ac45b703f"}, - {file = "orjson-3.10.12-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:3f250ce7727b0b2682f834a3facff88e310f52f07a5dcfd852d99637d386e79e"}, - {file = "orjson-3.10.12-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:f31422ff9486ae484f10ffc51b5ab2a60359e92d0716fcce1b3593d7bb8a9af6"}, - {file = "orjson-3.10.12-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5f29c5d282bb2d577c2a6bbde88d8fdcc4919c593f806aac50133f01b733846e"}, - {file = "orjson-3.10.12-cp313-none-win32.whl", hash = "sha256:f45653775f38f63dc0e6cd4f14323984c3149c05d6007b58cb154dd080ddc0dc"}, - {file = "orjson-3.10.12-cp313-none-win_amd64.whl", hash = "sha256:229994d0c376d5bdc91d92b3c9e6be2f1fbabd4cc1b59daae1443a46ee5e9825"}, - {file = "orjson-3.10.12-cp38-cp38-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:7d69af5b54617a5fac5c8e5ed0859eb798e2ce8913262eb522590239db6c6763"}, - {file = "orjson-3.10.12-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7ed119ea7d2953365724a7059231a44830eb6bbb0cfead33fcbc562f5fd8f935"}, - {file = "orjson-3.10.12-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9c5fc1238ef197e7cad5c91415f524aaa51e004be5a9b35a1b8a84ade196f73f"}, - {file = "orjson-3.10.12-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:43509843990439b05f848539d6f6198d4ac86ff01dd024b2f9a795c0daeeab60"}, - {file = "orjson-3.10.12-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f72e27a62041cfb37a3de512247ece9f240a561e6c8662276beaf4d53d406db4"}, - {file = "orjson-3.10.12-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9a904f9572092bb6742ab7c16c623f0cdccbad9eeb2d14d4aa06284867bddd31"}, - {file = "orjson-3.10.12-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:855c0833999ed5dc62f64552db26f9be767434917d8348d77bacaab84f787d7b"}, - {file = "orjson-3.10.12-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:897830244e2320f6184699f598df7fb9db9f5087d6f3f03666ae89d607e4f8ed"}, - {file = "orjson-3.10.12-cp38-cp38-musllinux_1_2_armv7l.whl", hash = "sha256:0b32652eaa4a7539f6f04abc6243619c56f8530c53bf9b023e1269df5f7816dd"}, - {file = "orjson-3.10.12-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:36b4aa31e0f6a1aeeb6f8377769ca5d125db000f05c20e54163aef1d3fe8e833"}, - {file = "orjson-3.10.12-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:5535163054d6cbf2796f93e4f0dbc800f61914c0e3c4ed8499cf6ece22b4a3da"}, - {file = "orjson-3.10.12-cp38-none-win32.whl", hash = "sha256:90a5551f6f5a5fa07010bf3d0b4ca2de21adafbbc0af6cb700b63cd767266cb9"}, - {file = "orjson-3.10.12-cp38-none-win_amd64.whl", hash = "sha256:703a2fb35a06cdd45adf5d733cf613cbc0cb3ae57643472b16bc22d325b5fb6c"}, - {file = "orjson-3.10.12-cp39-cp39-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:f29de3ef71a42a5822765def1febfb36e0859d33abf5c2ad240acad5c6a1b78d"}, - {file = "orjson-3.10.12-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:de365a42acc65d74953f05e4772c974dad6c51cfc13c3240899f534d611be967"}, - {file = "orjson-3.10.12-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:91a5a0158648a67ff0004cb0df5df7dcc55bfc9ca154d9c01597a23ad54c8d0c"}, - {file = "orjson-3.10.12-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c47ce6b8d90fe9646a25b6fb52284a14ff215c9595914af63a5933a49972ce36"}, - {file = "orjson-3.10.12-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0eee4c2c5bfb5c1b47a5db80d2ac7aaa7e938956ae88089f098aff2c0f35d5d8"}, - {file = "orjson-3.10.12-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:35d3081bbe8b86587eb5c98a73b97f13d8f9fea685cf91a579beddacc0d10566"}, - {file = "orjson-3.10.12-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:73c23a6e90383884068bc2dba83d5222c9fcc3b99a0ed2411d38150734236755"}, - {file = "orjson-3.10.12-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:5472be7dc3269b4b52acba1433dac239215366f89dc1d8d0e64029abac4e714e"}, - {file = "orjson-3.10.12-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:7319cda750fca96ae5973efb31b17d97a5c5225ae0bc79bf5bf84df9e1ec2ab6"}, - {file = "orjson-3.10.12-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:74d5ca5a255bf20b8def6a2b96b1e18ad37b4a122d59b154c458ee9494377f80"}, - {file = "orjson-3.10.12-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:ff31d22ecc5fb85ef62c7d4afe8301d10c558d00dd24274d4bbe464380d3cd69"}, - {file = "orjson-3.10.12-cp39-none-win32.whl", hash = "sha256:c22c3ea6fba91d84fcb4cda30e64aff548fcf0c44c876e681f47d61d24b12e6b"}, - {file = "orjson-3.10.12-cp39-none-win_amd64.whl", hash = "sha256:be604f60d45ace6b0b33dd990a66b4526f1a7a186ac411c942674625456ca548"}, - {file = "orjson-3.10.12.tar.gz", hash = "sha256:0a78bbda3aea0f9f079057ee1ee8a1ecf790d4f1af88dd67493c6b8ee52506ff"}, -] - [[package]] name = "outcome" version = "1.3.0.post0" @@ -3698,6 +3238,7 @@ files = [ {file = "psycopg2_binary-2.9.10-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:bb89f0a835bcfc1d42ccd5f41f04870c1b936d8507c6df12b7737febc40f0909"}, {file = "psycopg2_binary-2.9.10-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:f0c2d907a1e102526dd2986df638343388b94c33860ff3bbe1384130828714b1"}, {file = "psycopg2_binary-2.9.10-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f8157bed2f51db683f31306aa497311b560f2265998122abe1dce6428bd86567"}, + {file = "psycopg2_binary-2.9.10-cp313-cp313-win_amd64.whl", hash = "sha256:27422aa5f11fbcd9b18da48373eb67081243662f9b46e6fd07c3eb46e4535142"}, {file = "psycopg2_binary-2.9.10-cp38-cp38-macosx_12_0_x86_64.whl", hash = "sha256:eb09aa7f9cecb45027683bb55aebaaf45a0df8bf6de68801a6afdc7947bb09d4"}, {file = "psycopg2_binary-2.9.10-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b73d6d7f0ccdad7bc43e6d34273f70d587ef62f824d7261c4ae9b8b1b6af90e8"}, {file = "psycopg2_binary-2.9.10-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ce5ab4bf46a211a8e924d307c1b1fcda82368586a19d0a24f8ae166f5c784864"}, @@ -4115,17 +3656,6 @@ files = [ {file = "pytz-2024.2.tar.gz", hash = "sha256:2aa355083c50a0f93fa581709deac0c9ad65cca8a9e9beac660adcbd493c798a"}, ] -[[package]] -name = "pyvirtualdisplay" -version = "3.0" -description = "python wrapper for Xvfb, Xephyr and Xvnc" -optional = false -python-versions = "*" -files = [ - {file = "PyVirtualDisplay-3.0-py3-none-any.whl", hash = "sha256:40d4b8dfe4b8de8552e28eb367647f311f88a130bf837fe910e7f180d5477f0e"}, - {file = "PyVirtualDisplay-3.0.tar.gz", hash = "sha256:09755bc3ceb6eb725fb07eca5425f43f2358d3bf08e00d2a9b792a1aedd16159"}, -] - [[package]] name = "pyyaml" version = "6.0.2" @@ -4299,9 +3829,6 @@ files = [ {file = "redis-5.2.1.tar.gz", hash = "sha256:16f2e22dff21d5125e8481515e386711a34cbec50f0e44413dd7d9c060a54e0f"}, ] -[package.dependencies] -async-timeout = {version = ">=4.0.3", markers = "python_full_version < \"3.11.3\""} - [package.extras] hiredis = ["hiredis (>=3.0.0)"] ocsp = ["cryptography (>=36.0.1)", "pyopenssl (==23.2.1)", "requests (>=2.31.0)"] @@ -4478,31 +4005,109 @@ pyasn1 = ">=0.1.3" [[package]] name = "ruff" -version = "0.8.4" +version = "0.8.6" description = "An extremely fast Python linter and code formatter, written in Rust." optional = false python-versions = ">=3.7" files = [ - {file = "ruff-0.8.4-py3-none-linux_armv6l.whl", hash = "sha256:58072f0c06080276804c6a4e21a9045a706584a958e644353603d36ca1eb8a60"}, - {file = "ruff-0.8.4-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:ffb60904651c00a1e0b8df594591770018a0f04587f7deeb3838344fe3adabac"}, - {file = "ruff-0.8.4-py3-none-macosx_11_0_arm64.whl", hash = "sha256:6ddf5d654ac0d44389f6bf05cee4caeefc3132a64b58ea46738111d687352296"}, - {file = "ruff-0.8.4-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e248b1f0fa2749edd3350a2a342b67b43a2627434c059a063418e3d375cfe643"}, - {file = "ruff-0.8.4-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bf197b98ed86e417412ee3b6c893f44c8864f816451441483253d5ff22c0e81e"}, - {file = "ruff-0.8.4-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c41319b85faa3aadd4d30cb1cffdd9ac6b89704ff79f7664b853785b48eccdf3"}, - {file = "ruff-0.8.4-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:9f8402b7c4f96463f135e936d9ab77b65711fcd5d72e5d67597b543bbb43cf3f"}, - {file = "ruff-0.8.4-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e4e56b3baa9c23d324ead112a4fdf20db9a3f8f29eeabff1355114dd96014604"}, - {file = "ruff-0.8.4-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:736272574e97157f7edbbb43b1d046125fce9e7d8d583d5d65d0c9bf2c15addf"}, - {file = "ruff-0.8.4-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e5fe710ab6061592521f902fca7ebcb9fabd27bc7c57c764298b1c1f15fff720"}, - {file = "ruff-0.8.4-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:13e9ec6d6b55f6da412d59953d65d66e760d583dd3c1c72bf1f26435b5bfdbae"}, - {file = "ruff-0.8.4-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:97d9aefef725348ad77d6db98b726cfdb075a40b936c7984088804dfd38268a7"}, - {file = "ruff-0.8.4-py3-none-musllinux_1_2_i686.whl", hash = "sha256:ab78e33325a6f5374e04c2ab924a3367d69a0da36f8c9cb6b894a62017506111"}, - {file = "ruff-0.8.4-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:8ef06f66f4a05c3ddbc9121a8b0cecccd92c5bf3dd43b5472ffe40b8ca10f0f8"}, - {file = "ruff-0.8.4-py3-none-win32.whl", hash = "sha256:552fb6d861320958ca5e15f28b20a3d071aa83b93caee33a87b471f99a6c0835"}, - {file = "ruff-0.8.4-py3-none-win_amd64.whl", hash = "sha256:f21a1143776f8656d7f364bd264a9d60f01b7f52243fbe90e7670c0dfe0cf65d"}, - {file = "ruff-0.8.4-py3-none-win_arm64.whl", hash = "sha256:9183dd615d8df50defa8b1d9a074053891ba39025cf5ae88e8bcb52edcc4bf08"}, - {file = "ruff-0.8.4.tar.gz", hash = "sha256:0d5f89f254836799af1615798caa5f80b7f935d7a670fad66c5007928e57ace8"}, + {file = "ruff-0.8.6-py3-none-linux_armv6l.whl", hash = "sha256:defed167955d42c68b407e8f2e6f56ba52520e790aba4ca707a9c88619e580e3"}, + {file = "ruff-0.8.6-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:54799ca3d67ae5e0b7a7ac234baa657a9c1784b48ec954a094da7c206e0365b1"}, + {file = "ruff-0.8.6-py3-none-macosx_11_0_arm64.whl", hash = "sha256:e88b8f6d901477c41559ba540beeb5a671e14cd29ebd5683903572f4b40a9807"}, + {file = "ruff-0.8.6-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0509e8da430228236a18a677fcdb0c1f102dd26d5520f71f79b094963322ed25"}, + {file = "ruff-0.8.6-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:91a7ddb221779871cf226100e677b5ea38c2d54e9e2c8ed847450ebbdf99b32d"}, + {file = "ruff-0.8.6-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:248b1fb3f739d01d528cc50b35ee9c4812aa58cc5935998e776bf8ed5b251e75"}, + {file = "ruff-0.8.6-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:bc3c083c50390cf69e7e1b5a5a7303898966be973664ec0c4a4acea82c1d4315"}, + {file = "ruff-0.8.6-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:52d587092ab8df308635762386f45f4638badb0866355b2b86760f6d3c076188"}, + {file = "ruff-0.8.6-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:61323159cf21bc3897674e5adb27cd9e7700bab6b84de40d7be28c3d46dc67cf"}, + {file = "ruff-0.8.6-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7ae4478b1471fc0c44ed52a6fb787e641a2ac58b1c1f91763bafbc2faddc5117"}, + {file = "ruff-0.8.6-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:0c000a471d519b3e6cfc9c6680025d923b4ca140ce3e4612d1a2ef58e11f11fe"}, + {file = "ruff-0.8.6-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:9257aa841e9e8d9b727423086f0fa9a86b6b420fbf4bf9e1465d1250ce8e4d8d"}, + {file = "ruff-0.8.6-py3-none-musllinux_1_2_i686.whl", hash = "sha256:45a56f61b24682f6f6709636949ae8cc82ae229d8d773b4c76c09ec83964a95a"}, + {file = "ruff-0.8.6-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:496dd38a53aa173481a7d8866bcd6451bd934d06976a2505028a50583e001b76"}, + {file = "ruff-0.8.6-py3-none-win32.whl", hash = "sha256:e169ea1b9eae61c99b257dc83b9ee6c76f89042752cb2d83486a7d6e48e8f764"}, + {file = "ruff-0.8.6-py3-none-win_amd64.whl", hash = "sha256:f1d70bef3d16fdc897ee290d7d20da3cbe4e26349f62e8a0274e7a3f4ce7a905"}, + {file = "ruff-0.8.6-py3-none-win_arm64.whl", hash = "sha256:7d7fc2377a04b6e04ffe588caad613d0c460eb2ecba4c0ccbbfe2bc973cbc162"}, + {file = "ruff-0.8.6.tar.gz", hash = "sha256:dcad24b81b62650b0eb8814f576fc65cfee8674772a6e24c9b747911801eeaa5"}, +] + +[[package]] +name = "scout-apm" +version = "3.2.1" +description = "Scout Application Performance Monitoring Agent" +optional = false +python-versions = "<4,>=3.8" +files = [ + {file = "scout_apm-3.2.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7f2b13d508f5d5dc590ea392066acd366696370a9866d50217bb6fbff385cf5b"}, + {file = "scout_apm-3.2.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:386f6caa3db81211a7f197322da041c87d9623ee0eedf724b79ffd34867a7b22"}, + {file = "scout_apm-3.2.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fff3c9d05a099ab3c1c8390e8b9079aa7ee58a48793ba561163b14d228adc6d3"}, + {file = "scout_apm-3.2.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9757f4e2118385b7f91ee29cc15a55b5ae807044d9f035b1f066aeeb5ef9e784"}, + {file = "scout_apm-3.2.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2fc81d7b65cd291ab022a32f3e5b0fa06b7108ced73f99bec712d7a7a973e385"}, + {file = "scout_apm-3.2.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5a89c5e619b5505eb811a3aab6c0d2ea7eb117f503a234253b8a4a4f678f5216"}, + {file = "scout_apm-3.2.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2135c6e64813b424f38263f622f009a8f61555017aba235779d9088cc8c75e64"}, + {file = "scout_apm-3.2.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:5ce0e8a60e9f3961272116fab862b0c61fbc73721281e061b352b376ad72441d"}, + {file = "scout_apm-3.2.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:5b5ef6fca89655eb7789d5a57857019f945a2cf96fffc955b76857386a6e8d98"}, + {file = "scout_apm-3.2.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:6076a58b5390d93ab2e5ffe5f9ce72653123f4cde8cd0a7a320d5e617c5ae2c6"}, + {file = "scout_apm-3.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:78e480dae8ca233c563885f63743fcbf4551ccc5fd3d573e18442def6b985cdb"}, + {file = "scout_apm-3.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6cb7e9a37c513b85317f99d72d10dc6ef044df441496ae0fc576762931cf60e4"}, + {file = "scout_apm-3.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bbdfa1b8cda52fa87d787fc76a96a81e8da9872a6ad07d52631497c87bb44dd0"}, + {file = "scout_apm-3.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bf769369d6cfe03aa72762662cc2e07a3a71425675bb605ffae2f8b68acc9f2c"}, + {file = "scout_apm-3.2.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f7cd5ea92735e1651e8a5396493c3c6122268ed27bd6e6208e69386558befac1"}, + {file = "scout_apm-3.2.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:87a6b2d5f1e5a34cf6928ea04680125026d366f1ebb25cb9d722f553c51d389c"}, + {file = "scout_apm-3.2.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8e59e9ea77af9ad23a9d09082326015a52be00e13fdc38aa291543d030b1bced"}, + {file = "scout_apm-3.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:13c1cfb241b78b57c36a3dcfdd69567137ce4fb61cdfb3279dac926cd8028cde"}, + {file = "scout_apm-3.2.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:337612cd915d11a6b8cc302edf8ee0da881fa00194a0c1b291ab07a4ef52d280"}, + {file = "scout_apm-3.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:29bc0b7340b4775bb30a6d34f935130588d68483fec164c1de6d53ab64071542"}, + {file = "scout_apm-3.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:9ce6aeb810acfe5ecc7f485257e1b19705db074e5c405c2f72e38efa64e59d4c"}, + {file = "scout_apm-3.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4f6b08defc72a3dd57791d02f9741be97e7b353c965f0f922360ec58e3ea6200"}, + {file = "scout_apm-3.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f35a5536c1eb32f6ea7b708462325bcdc99268e4145bc6a0afe886badd9cf18f"}, + {file = "scout_apm-3.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:28148b6e61468dc6df0677516b06844d2fe8faa2b044567efc6b96d7da218a36"}, + {file = "scout_apm-3.2.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f45c61f75f83c21420327097c7780cd84c766b3a26ac1e83d4ec95dfa672ed9c"}, + {file = "scout_apm-3.2.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e5582f9996ab71346513ad36a6fd19bf8ccdae44d503120dc68d86802dfb524b"}, + {file = "scout_apm-3.2.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:07926e68e06fd9418a755c9ca78eb127ce677d85b5243b40d177af218c31c3bc"}, + {file = "scout_apm-3.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d67a8821ab6ab659dd6b0afa4d6345d61fe04b672d11c1150b5f2f1cd857c68b"}, + {file = "scout_apm-3.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:7808c08c24d2e2be46835e547465574dfa5a0abfff164eef1dd581fe71168e67"}, + {file = "scout_apm-3.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:93c09eb5052d14ebb0ea7f4541c22cbb21bf814fcf20263f21bed870748bd6cb"}, + {file = "scout_apm-3.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:38ccb0a498e1d9d7579fb83965f60ae5837962cea0fa99a43cbedca1095df3f3"}, + {file = "scout_apm-3.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7a3dae1c79561eace8903f0650c1ffe56763db3352edf7f262c5e2c6dc7a190e"}, + {file = "scout_apm-3.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cf1a5ba684ac2613759bd706b847df72645299fc3e365a6e12675d6f1ffe7396"}, + {file = "scout_apm-3.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:caf874e599ab7ddd09d6df6cdec16fed741159e170afd679b2379b5dbf194907"}, + {file = "scout_apm-3.2.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9601011de7c2389934d9b535cab5e431e0428824624a391aa43a6cc803ec37b"}, + {file = "scout_apm-3.2.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95e428292d6158a0360d08be8215e0f7d6630b9b2429110ea839cf828de3e58e"}, + {file = "scout_apm-3.2.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5a35e88d190bdc501b217fb271c42ae3637184c8835b386f6751c34f00b579bf"}, + {file = "scout_apm-3.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d54ff09267d80fe189ae99b6739af8cf0a0085c7263055e716bdb51b8e4f23ad"}, + {file = "scout_apm-3.2.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:24d0f1f0ec684829a3be3b36e00ec9663e45bbcd38d9c6acda6f4b6413a24d3d"}, + {file = "scout_apm-3.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:3e733257b817e7fddd3d8f57e340d4765cf02c58a6a7fd8ad55823041ab34a42"}, + {file = "scout_apm-3.2.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f9b4f8c7b3bafd3a6650b5a52c6b5f30f554da20b1879e789eb929d37440fdcf"}, + {file = "scout_apm-3.2.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:a2267be02c56738fe8056abfbf7ff4db0f18427b1124b3709c39c7d9ccfd3062"}, + {file = "scout_apm-3.2.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b383eac6b96bae8732b7d8243ba1bf08d8bc04f7d5e4f0fe4dda92af4fed517f"}, + {file = "scout_apm-3.2.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d05780a6cfe6762a72493c9e3dad62311a96c4e37c883bb750d1c7405e985d1a"}, + {file = "scout_apm-3.2.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ce08a5d6bea569cf441f5dca87974f37e890307db10506aa8ad3c03d459e0f14"}, + {file = "scout_apm-3.2.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ad7ac9eeda31f3ddd53fac0d5055e125055cc42d7618f971d37cd64eecb561ab"}, + {file = "scout_apm-3.2.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9933edef14c729c902a932c94a3b84da97c4aede197c0e8447f66c9eb601c4b0"}, + {file = "scout_apm-3.2.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:2d7aeb8fe127490fe2f0c366a92a4fd6073113c8fb62e15ecb744804fc9d3b2f"}, + {file = "scout_apm-3.2.1-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:2fb732f5ab0bfc200485f45a80d786efcc2881a2a4a7f03cde2df85bbe243464"}, + {file = "scout_apm-3.2.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:5beccd63927d6cbc67f894e86fe74ac10e96fbca7c925ec6e9db2c21f7f40673"}, + {file = "scout_apm-3.2.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:59dabfb4aa4917a96e9199c1749e329115987b789026e52c8db6f64e968616f1"}, + {file = "scout_apm-3.2.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:6015ad17b43624d6023b50675e0ac5d1c323ed29f969dc2a1a38d9192c7b3732"}, + {file = "scout_apm-3.2.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:db70fcfbd805892675561e413a38d1d0dbaec86bb1be24cc9a80372dccfa1074"}, + {file = "scout_apm-3.2.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9fd6ee26378916548bbe422fe8c94bd1fbbcee0cb06ff64fd07855747cef6306"}, + {file = "scout_apm-3.2.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:72af70a094f6c7082b54d0a677a31cd1615af70dfb5e8246a1491c6ffc3770d4"}, + {file = "scout_apm-3.2.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b8de2ebbdcc51039d977fefcfe9811efa680f65305a5dc3e0905ff27f7c2152"}, + {file = "scout_apm-3.2.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5c9452fa6ba1f4ed6111cc9e7d393842b023f747cd1977dab2fc55701ebb83c8"}, + {file = "scout_apm-3.2.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:8236bed9104b912b476d7769af354f14d4d5cdd0bf2f0ed029a74fc2896f0574"}, + {file = "scout_apm-3.2.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:aba37ce89dd9bf90b599f5a7ea7b12bad9386937d6fd5e41642b2b831377c469"}, + {file = "scout_apm-3.2.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:0fbc8ee23f13b5d8a49567b5bb33d334b78f58b143b789c4608824a984adef08"}, + {file = "scout_apm-3.2.1-py3-none-any.whl", hash = "sha256:b39016be3f84de6b92501dc735bd686129f4e2ed8c05eac4632e7b365d8c58d4"}, + {file = "scout_apm-3.2.1.tar.gz", hash = "sha256:7f1da37cc1c0b3a5fceb279df635dd84a8b3537e3712df94749ff9ed1c52a28c"}, ] +[package.dependencies] +asgiref = "*" +certifi = "*" +psutil = ">=5,<6" +urllib3 = "*" +wrapt = ">=1.10,<2.0" + [[package]] name = "selenium" version = "4.27.1" @@ -4630,17 +4235,6 @@ files = [ [package.extras] optional = ["SQLAlchemy (>=1.4,<3)", "aiodns (>1.0)", "aiohttp (>=3.7.3,<4)", "boto3 (<=2)", "websocket-client (>=1,<2)", "websockets (>=9.1,<15)"] -[[package]] -name = "smmap" -version = "5.0.1" -description = "A pure Python implementation of a sliding window memory map manager" -optional = false -python-versions = ">=3.7" -files = [ - {file = "smmap-5.0.1-py3-none-any.whl", hash = "sha256:e6d8668fa5f93e706934a62d7b4db19c8d9eb8cf2adbb75ef1b675aa332b69da"}, - {file = "smmap-5.0.1.tar.gz", hash = "sha256:dceeb6c0028fdb6734471eb07c0cd2aae706ccaecab45965ee83f11c8d3b1f62"}, -] - [[package]] name = "sniffio" version = "1.3.1" @@ -4674,101 +4268,6 @@ files = [ {file = "soupsieve-2.6.tar.gz", hash = "sha256:e2e68417777af359ec65daac1057404a3c8a5455bb8abc36f1a9866ab1a51abb"}, ] -[[package]] -name = "sqlalchemy" -version = "2.0.36" -description = "Database Abstraction Library" -optional = false -python-versions = ">=3.7" -files = [ - {file = "SQLAlchemy-2.0.36-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:59b8f3adb3971929a3e660337f5dacc5942c2cdb760afcabb2614ffbda9f9f72"}, - {file = "SQLAlchemy-2.0.36-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:37350015056a553e442ff672c2d20e6f4b6d0b2495691fa239d8aa18bb3bc908"}, - {file = "SQLAlchemy-2.0.36-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8318f4776c85abc3f40ab185e388bee7a6ea99e7fa3a30686580b209eaa35c08"}, - {file = "SQLAlchemy-2.0.36-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c245b1fbade9c35e5bd3b64270ab49ce990369018289ecfde3f9c318411aaa07"}, - {file = "SQLAlchemy-2.0.36-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:69f93723edbca7342624d09f6704e7126b152eaed3cdbb634cb657a54332a3c5"}, - {file = "SQLAlchemy-2.0.36-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f9511d8dd4a6e9271d07d150fb2f81874a3c8c95e11ff9af3a2dfc35fe42ee44"}, - {file = "SQLAlchemy-2.0.36-cp310-cp310-win32.whl", hash = "sha256:c3f3631693003d8e585d4200730616b78fafd5a01ef8b698f6967da5c605b3fa"}, - {file = "SQLAlchemy-2.0.36-cp310-cp310-win_amd64.whl", hash = "sha256:a86bfab2ef46d63300c0f06936bd6e6c0105faa11d509083ba8f2f9d237fb5b5"}, - {file = "SQLAlchemy-2.0.36-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:fd3a55deef00f689ce931d4d1b23fa9f04c880a48ee97af488fd215cf24e2a6c"}, - {file = "SQLAlchemy-2.0.36-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4f5e9cd989b45b73bd359f693b935364f7e1f79486e29015813c338450aa5a71"}, - {file = "SQLAlchemy-2.0.36-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d0ddd9db6e59c44875211bc4c7953a9f6638b937b0a88ae6d09eb46cced54eff"}, - {file = "SQLAlchemy-2.0.36-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2519f3a5d0517fc159afab1015e54bb81b4406c278749779be57a569d8d1bb0d"}, - {file = "SQLAlchemy-2.0.36-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:59b1ee96617135f6e1d6f275bbe988f419c5178016f3d41d3c0abb0c819f75bb"}, - {file = "SQLAlchemy-2.0.36-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:39769a115f730d683b0eb7b694db9789267bcd027326cccc3125e862eb03bfd8"}, - {file = "SQLAlchemy-2.0.36-cp311-cp311-win32.whl", hash = "sha256:66bffbad8d6271bb1cc2f9a4ea4f86f80fe5e2e3e501a5ae2a3dc6a76e604e6f"}, - {file = "SQLAlchemy-2.0.36-cp311-cp311-win_amd64.whl", hash = "sha256:23623166bfefe1487d81b698c423f8678e80df8b54614c2bf4b4cfcd7c711959"}, - {file = "SQLAlchemy-2.0.36-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f7b64e6ec3f02c35647be6b4851008b26cff592a95ecb13b6788a54ef80bbdd4"}, - {file = "SQLAlchemy-2.0.36-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:46331b00096a6db1fdc052d55b101dbbfc99155a548e20a0e4a8e5e4d1362855"}, - {file = "SQLAlchemy-2.0.36-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fdf3386a801ea5aba17c6410dd1dc8d39cf454ca2565541b5ac42a84e1e28f53"}, - {file = "SQLAlchemy-2.0.36-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ac9dfa18ff2a67b09b372d5db8743c27966abf0e5344c555d86cc7199f7ad83a"}, - {file = "SQLAlchemy-2.0.36-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:90812a8933df713fdf748b355527e3af257a11e415b613dd794512461eb8a686"}, - {file = "SQLAlchemy-2.0.36-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1bc330d9d29c7f06f003ab10e1eaced295e87940405afe1b110f2eb93a233588"}, - {file = "SQLAlchemy-2.0.36-cp312-cp312-win32.whl", hash = "sha256:79d2e78abc26d871875b419e1fd3c0bca31a1cb0043277d0d850014599626c2e"}, - {file = "SQLAlchemy-2.0.36-cp312-cp312-win_amd64.whl", hash = "sha256:b544ad1935a8541d177cb402948b94e871067656b3a0b9e91dbec136b06a2ff5"}, - {file = "SQLAlchemy-2.0.36-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:b5cc79df7f4bc3d11e4b542596c03826063092611e481fcf1c9dfee3c94355ef"}, - {file = "SQLAlchemy-2.0.36-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:3c01117dd36800f2ecaa238c65365b7b16497adc1522bf84906e5710ee9ba0e8"}, - {file = "SQLAlchemy-2.0.36-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9bc633f4ee4b4c46e7adcb3a9b5ec083bf1d9a97c1d3854b92749d935de40b9b"}, - {file = "SQLAlchemy-2.0.36-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e46ed38affdfc95d2c958de328d037d87801cfcbea6d421000859e9789e61c2"}, - {file = "SQLAlchemy-2.0.36-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:b2985c0b06e989c043f1dc09d4fe89e1616aadd35392aea2844f0458a989eacf"}, - {file = "SQLAlchemy-2.0.36-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4a121d62ebe7d26fec9155f83f8be5189ef1405f5973ea4874a26fab9f1e262c"}, - {file = "SQLAlchemy-2.0.36-cp313-cp313-win32.whl", hash = "sha256:0572f4bd6f94752167adfd7c1bed84f4b240ee6203a95e05d1e208d488d0d436"}, - {file = "SQLAlchemy-2.0.36-cp313-cp313-win_amd64.whl", hash = "sha256:8c78ac40bde930c60e0f78b3cd184c580f89456dd87fc08f9e3ee3ce8765ce88"}, - {file = "SQLAlchemy-2.0.36-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:be9812b766cad94a25bc63bec11f88c4ad3629a0cec1cd5d4ba48dc23860486b"}, - {file = "SQLAlchemy-2.0.36-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50aae840ebbd6cdd41af1c14590e5741665e5272d2fee999306673a1bb1fdb4d"}, - {file = "SQLAlchemy-2.0.36-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4557e1f11c5f653ebfdd924f3f9d5ebfc718283b0b9beebaa5dd6b77ec290971"}, - {file = "SQLAlchemy-2.0.36-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:07b441f7d03b9a66299ce7ccf3ef2900abc81c0db434f42a5694a37bd73870f2"}, - {file = "SQLAlchemy-2.0.36-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:28120ef39c92c2dd60f2721af9328479516844c6b550b077ca450c7d7dc68575"}, - {file = "SQLAlchemy-2.0.36-cp37-cp37m-win32.whl", hash = "sha256:b81ee3d84803fd42d0b154cb6892ae57ea6b7c55d8359a02379965706c7efe6c"}, - {file = "SQLAlchemy-2.0.36-cp37-cp37m-win_amd64.whl", hash = "sha256:f942a799516184c855e1a32fbc7b29d7e571b52612647866d4ec1c3242578fcb"}, - {file = "SQLAlchemy-2.0.36-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:3d6718667da04294d7df1670d70eeddd414f313738d20a6f1d1f379e3139a545"}, - {file = "SQLAlchemy-2.0.36-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:72c28b84b174ce8af8504ca28ae9347d317f9dba3999e5981a3cd441f3712e24"}, - {file = "SQLAlchemy-2.0.36-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b11d0cfdd2b095e7b0686cf5fabeb9c67fae5b06d265d8180715b8cfa86522e3"}, - {file = "SQLAlchemy-2.0.36-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e32092c47011d113dc01ab3e1d3ce9f006a47223b18422c5c0d150af13a00687"}, - {file = "SQLAlchemy-2.0.36-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:6a440293d802d3011028e14e4226da1434b373cbaf4a4bbb63f845761a708346"}, - {file = "SQLAlchemy-2.0.36-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:c54a1e53a0c308a8e8a7dffb59097bff7facda27c70c286f005327f21b2bd6b1"}, - {file = "SQLAlchemy-2.0.36-cp38-cp38-win32.whl", hash = "sha256:1e0d612a17581b6616ff03c8e3d5eff7452f34655c901f75d62bd86449d9750e"}, - {file = "SQLAlchemy-2.0.36-cp38-cp38-win_amd64.whl", hash = "sha256:8958b10490125124463095bbdadda5aa22ec799f91958e410438ad6c97a7b793"}, - {file = "SQLAlchemy-2.0.36-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:dc022184d3e5cacc9579e41805a681187650e170eb2fd70e28b86192a479dcaa"}, - {file = "SQLAlchemy-2.0.36-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b817d41d692bf286abc181f8af476c4fbef3fd05e798777492618378448ee689"}, - {file = "SQLAlchemy-2.0.36-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a4e46a888b54be23d03a89be510f24a7652fe6ff660787b96cd0e57a4ebcb46d"}, - {file = "SQLAlchemy-2.0.36-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c4ae3005ed83f5967f961fd091f2f8c5329161f69ce8480aa8168b2d7fe37f06"}, - {file = "SQLAlchemy-2.0.36-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:03e08af7a5f9386a43919eda9de33ffda16b44eb11f3b313e6822243770e9763"}, - {file = "SQLAlchemy-2.0.36-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:3dbb986bad3ed5ceaf090200eba750b5245150bd97d3e67343a3cfed06feecf7"}, - {file = "SQLAlchemy-2.0.36-cp39-cp39-win32.whl", hash = "sha256:9fe53b404f24789b5ea9003fc25b9a3988feddebd7e7b369c8fac27ad6f52f28"}, - {file = "SQLAlchemy-2.0.36-cp39-cp39-win_amd64.whl", hash = "sha256:af148a33ff0349f53512a049c6406923e4e02bf2f26c5fb285f143faf4f0e46a"}, - {file = "SQLAlchemy-2.0.36-py3-none-any.whl", hash = "sha256:fddbe92b4760c6f5d48162aef14824add991aeda8ddadb3c31d56eb15ca69f8e"}, - {file = "sqlalchemy-2.0.36.tar.gz", hash = "sha256:7f2767680b6d2398aea7082e45a774b2b0767b5c8d8ffb9c8b683088ea9b29c5"}, -] - -[package.dependencies] -greenlet = {version = "!=0.4.17", markers = "python_version < \"3.13\" and (platform_machine == \"aarch64\" or platform_machine == \"ppc64le\" or platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"AMD64\" or platform_machine == \"win32\" or platform_machine == \"WIN32\")"} -typing-extensions = ">=4.6.0" - -[package.extras] -aiomysql = ["aiomysql (>=0.2.0)", "greenlet (!=0.4.17)"] -aioodbc = ["aioodbc", "greenlet (!=0.4.17)"] -aiosqlite = ["aiosqlite", "greenlet (!=0.4.17)", "typing_extensions (!=3.10.0.1)"] -asyncio = ["greenlet (!=0.4.17)"] -asyncmy = ["asyncmy (>=0.2.3,!=0.2.4,!=0.2.6)", "greenlet (!=0.4.17)"] -mariadb-connector = ["mariadb (>=1.0.1,!=1.1.2,!=1.1.5,!=1.1.10)"] -mssql = ["pyodbc"] -mssql-pymssql = ["pymssql"] -mssql-pyodbc = ["pyodbc"] -mypy = ["mypy (>=0.910)"] -mysql = ["mysqlclient (>=1.4.0)"] -mysql-connector = ["mysql-connector-python"] -oracle = ["cx_oracle (>=8)"] -oracle-oracledb = ["oracledb (>=1.0.1)"] -postgresql = ["psycopg2 (>=2.7)"] -postgresql-asyncpg = ["asyncpg", "greenlet (!=0.4.17)"] -postgresql-pg8000 = ["pg8000 (>=1.29.1)"] -postgresql-psycopg = ["psycopg (>=3.0.7)"] -postgresql-psycopg2binary = ["psycopg2-binary"] -postgresql-psycopg2cffi = ["psycopg2cffi"] -postgresql-psycopgbinary = ["psycopg[binary] (>=3.0.7)"] -pymysql = ["pymysql"] -sqlcipher = ["sqlcipher3_binary"] - [[package]] name = "sqlparse" version = "0.5.3" @@ -4840,68 +4339,6 @@ xls = ["xlrd", "xlwt"] xlsx = ["openpyxl (>=2.6.0)"] yaml = ["pyyaml"] -[[package]] -name = "tenacity" -version = "8.5.0" -description = "Retry code until it succeeds" -optional = false -python-versions = ">=3.8" -files = [ - {file = "tenacity-8.5.0-py3-none-any.whl", hash = "sha256:b594c2a5945830c267ce6b79a166228323ed52718f30302c1359836112346687"}, - {file = "tenacity-8.5.0.tar.gz", hash = "sha256:8bc6c0c8a09b31e6cad13c47afbed1a567518250a9a171418582ed8d9c20ca78"}, -] - -[package.extras] -doc = ["reno", "sphinx"] -test = ["pytest", "tornado (>=4.5)", "typeguard"] - -[[package]] -name = "tiktoken" -version = "0.8.0" -description = "tiktoken is a fast BPE tokeniser for use with OpenAI's models" -optional = false -python-versions = ">=3.9" -files = [ - {file = "tiktoken-0.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b07e33283463089c81ef1467180e3e00ab00d46c2c4bbcef0acab5f771d6695e"}, - {file = "tiktoken-0.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9269348cb650726f44dd3bbb3f9110ac19a8dcc8f54949ad3ef652ca22a38e21"}, - {file = "tiktoken-0.8.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25e13f37bc4ef2d012731e93e0fef21dc3b7aea5bb9009618de9a4026844e560"}, - {file = "tiktoken-0.8.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f13d13c981511331eac0d01a59b5df7c0d4060a8be1e378672822213da51e0a2"}, - {file = "tiktoken-0.8.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:6b2ddbc79a22621ce8b1166afa9f9a888a664a579350dc7c09346a3b5de837d9"}, - {file = "tiktoken-0.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:d8c2d0e5ba6453a290b86cd65fc51fedf247e1ba170191715b049dac1f628005"}, - {file = "tiktoken-0.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d622d8011e6d6f239297efa42a2657043aaed06c4f68833550cac9e9bc723ef1"}, - {file = "tiktoken-0.8.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2efaf6199717b4485031b4d6edb94075e4d79177a172f38dd934d911b588d54a"}, - {file = "tiktoken-0.8.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5637e425ce1fc49cf716d88df3092048359a4b3bbb7da762840426e937ada06d"}, - {file = "tiktoken-0.8.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fb0e352d1dbe15aba082883058b3cce9e48d33101bdaac1eccf66424feb5b47"}, - {file = "tiktoken-0.8.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:56edfefe896c8f10aba372ab5706b9e3558e78db39dd497c940b47bf228bc419"}, - {file = "tiktoken-0.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:326624128590def898775b722ccc327e90b073714227175ea8febbc920ac0a99"}, - {file = "tiktoken-0.8.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:881839cfeae051b3628d9823b2e56b5cc93a9e2efb435f4cf15f17dc45f21586"}, - {file = "tiktoken-0.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fe9399bdc3f29d428f16a2f86c3c8ec20be3eac5f53693ce4980371c3245729b"}, - {file = "tiktoken-0.8.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9a58deb7075d5b69237a3ff4bb51a726670419db6ea62bdcd8bd80c78497d7ab"}, - {file = "tiktoken-0.8.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2908c0d043a7d03ebd80347266b0e58440bdef5564f84f4d29fb235b5df3b04"}, - {file = "tiktoken-0.8.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:294440d21a2a51e12d4238e68a5972095534fe9878be57d905c476017bff99fc"}, - {file = "tiktoken-0.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:d8f3192733ac4d77977432947d563d7e1b310b96497acd3c196c9bddb36ed9db"}, - {file = "tiktoken-0.8.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:02be1666096aff7da6cbd7cdaa8e7917bfed3467cd64b38b1f112e96d3b06a24"}, - {file = "tiktoken-0.8.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:c94ff53c5c74b535b2cbf431d907fc13c678bbd009ee633a2aca269a04389f9a"}, - {file = "tiktoken-0.8.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6b231f5e8982c245ee3065cd84a4712d64692348bc609d84467c57b4b72dcbc5"}, - {file = "tiktoken-0.8.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4177faa809bd55f699e88c96d9bb4635d22e3f59d635ba6fd9ffedf7150b9953"}, - {file = "tiktoken-0.8.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5376b6f8dc4753cd81ead935c5f518fa0fbe7e133d9e25f648d8c4dabdd4bad7"}, - {file = "tiktoken-0.8.0-cp313-cp313-win_amd64.whl", hash = "sha256:18228d624807d66c87acd8f25fc135665617cab220671eb65b50f5d70fa51f69"}, - {file = "tiktoken-0.8.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7e17807445f0cf1f25771c9d86496bd8b5c376f7419912519699f3cc4dc5c12e"}, - {file = "tiktoken-0.8.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:886f80bd339578bbdba6ed6d0567a0d5c6cfe198d9e587ba6c447654c65b8edc"}, - {file = "tiktoken-0.8.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6adc8323016d7758d6de7313527f755b0fc6c72985b7d9291be5d96d73ecd1e1"}, - {file = "tiktoken-0.8.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b591fb2b30d6a72121a80be24ec7a0e9eb51c5500ddc7e4c2496516dd5e3816b"}, - {file = "tiktoken-0.8.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:845287b9798e476b4d762c3ebda5102be87ca26e5d2c9854002825d60cdb815d"}, - {file = "tiktoken-0.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:1473cfe584252dc3fa62adceb5b1c763c1874e04511b197da4e6de51d6ce5a02"}, - {file = "tiktoken-0.8.0.tar.gz", hash = "sha256:9ccbb2740f24542534369c5635cfd9b2b3c2490754a78ac8831d99f89f94eeb2"}, -] - -[package.dependencies] -regex = ">=2022.1.18" -requests = ">=2.26.0" - -[package.extras] -blobfile = ["blobfile (>=2)"] - [[package]] name = "toml" version = "0.10.2" @@ -4936,13 +4373,13 @@ telegram = ["requests"] [[package]] name = "trio" -version = "0.27.0" +version = "0.28.0" description = "A friendly Python library for async concurrency and I/O" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "trio-0.27.0-py3-none-any.whl", hash = "sha256:68eabbcf8f457d925df62da780eff15ff5dc68fd6b367e2dde59f7aaf2a0b884"}, - {file = "trio-0.27.0.tar.gz", hash = "sha256:1dcc95ab1726b2da054afea8fd761af74bad79bd52381b84eae408e983c76831"}, + {file = "trio-0.28.0-py3-none-any.whl", hash = "sha256:56d58977acc1635735a96581ec70513cc781b8b6decd299c487d3be2a721cd94"}, + {file = "trio-0.28.0.tar.gz", hash = "sha256:4e547896fe9e8a5658e54e4c7c5fa1db748cbbbaa7c965e7d40505b928c73c05"}, ] [package.dependencies] @@ -5070,13 +4507,13 @@ files = [ [[package]] name = "unstructured" -version = "0.16.11" +version = "0.16.12" description = "A library that prepares raw documents for downstream ML tasks." optional = false python-versions = "<3.13,>=3.9.0" files = [ - {file = "unstructured-0.16.11-py3-none-any.whl", hash = "sha256:a92d5bc2c2b7bb23369641fb7a7f0daba1775639199306ce4cd83ca564a03763"}, - {file = "unstructured-0.16.11.tar.gz", hash = "sha256:33ebf68aae11ce33c8a96335296557b5abd8ba96eaba3e5a1554c0b9eee40bb5"}, + {file = "unstructured-0.16.12-py3-none-any.whl", hash = "sha256:bcac29ac1b38fba4228c5a1a7721d1aa7c48220f7c1dd43b563645c56e978c49"}, + {file = "unstructured-0.16.12.tar.gz", hash = "sha256:c3133731c6edb9c2f474e62cb2b560cd0a8d578c4532ec14d8c0941e401770b0"}, ] [package.dependencies] @@ -5089,6 +4526,7 @@ filetype = "*" html5lib = "*" langdetect = "*" lxml = "*" +ndjson = "*" nltk = "*" numpy = "<2" psutil = "*" @@ -5581,4 +5019,4 @@ propcache = ">=0.2.0" [metadata] lock-version = "2.0" python-versions = "3.11.2" -content-hash = "4062c5024e6472379be149250c7a5a2f420c30664d663d23e3aab0fcf9256a11" +content-hash = "807c866bfcd861d24cbd2a95912e12845ca06a689199504ba971618fa27d3b62" diff --git a/pyproject.toml b/pyproject.toml index 0308ca31d..f74479c75 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -40,7 +40,6 @@ oauthlib = "^3.2.0" parse = "^1.20.2" python-openid = "^2.2.5" pytz = "^2024.1" -PyVirtualDisplay = "^3.0" requests = "^2.32.3" requests-oauthlib = "^1.3.1" six = "^1.16.0" @@ -66,14 +65,9 @@ sentry-sdk = "^2.19.0" bitcash = "^1.0.2" pydantic = "^2.7.3" pydantic_core = "^2.18.4" -langchain = "^0.2.17" -langchain-community = "^0.2.19" -langchain-core = "^0.2.43" -langchain-openai = "^0.1.25" -unstructured = "^0.16.8" +unstructured = "^0.16.12" Markdown = "^3.6" faiss-cpu = "^1.8.0" -openai = "^1.58.1" psutil = "^5.9.8" python-bitcoinrpc = "^1.0" sendgrid = "^6.11.0" @@ -85,12 +79,15 @@ django-redis = "^5.4.0" uvicorn = "^0.34.0" channels = "^4.2.0" channels-redis = "^4.2.1" -gitpython = "^3.1.43" +async-timeout = "^5.0.1" +aiohttp = "^3.11.11" +scout-apm = "^3.2.1" +newrelic = "^10.4.0" [tool.poetry.group.dev.dependencies] black = "^24.8.0" isort = "^5.13.2" -ruff = "^0.8.4" +ruff = "^0.8.6" pre-commit = "^3.8.0" [tool.isort] diff --git a/run.sh b/run.sh index 61b44e96d..121a4a569 100755 --- a/run.sh +++ b/run.sh @@ -1 +1 @@ -uvicorn blt.asgi:application --host 0.0.0.0 --port 8000 \ No newline at end of file +uvicorn blt.asgi:application --host 0.0.0.0 --port 8000 --log-level debug \ No newline at end of file diff --git a/website/consumers.py b/website/consumers.py index 30d5eed86..517544290 100644 --- a/website/consumers.py +++ b/website/consumers.py @@ -2,11 +2,12 @@ import difflib import json import os -import subprocess import tempfile +import zipfile +from pathlib import Path +import aiohttp from channels.generic.websocket import AsyncWebsocketConsumer -from git import Repo from website.utils import ( compare_model_fields, @@ -14,6 +15,7 @@ extract_django_models, extract_function_signatures_and_content, generate_embedding, + git_url_to_zip_url, ) @@ -46,6 +48,8 @@ async def receive(self, text_data): type2 = data.get("type2") # 'github' or 'zip' repo1 = data.get("repo1") # GitHub URL or ZIP file path repo2 = data.get("repo2") # GitHub URL or ZIP file path + branch1 = data.get("branch1") # Branch name for the first repository + branch2 = data.get("branch2") # Branch name for the second repository if not repo1 or not repo2 or not type1 or not type2: await self.send( @@ -58,12 +62,14 @@ async def receive(self, text_data): return try: - # Create a temporary directory for repository processing temp_dir = tempfile.mkdtemp() # Download or extract the repositories - repo1_path = await self.download_or_extract(repo1, type1, temp_dir, "repo1") - repo2_path = await self.download_or_extract(repo2, type2, temp_dir, "repo2") + + zip_repo1 = git_url_to_zip_url(repo1, branch1) + zip_repo2 = git_url_to_zip_url(repo2, branch2) + repo1_path = await self.download_or_extract(zip_repo1, type1, temp_dir, "repo1") + repo2_path = await self.download_or_extract(zip_repo2, type2, temp_dir, "repo2") # Process similarity analysis matching_details = await self.run_similarity_analysis(repo1_path, repo2_path) @@ -88,7 +94,10 @@ async def receive(self, text_data): # Handle unexpected errors and send an error message await self.send( json.dumps( - {"status": "error", "error": "Please check the repositories and try again."} + { + "status": "error", + "error": "Please check the repositories/branches and try again.", + } ) ) await self.close() @@ -125,29 +134,62 @@ async def download_or_extract(self, source, source_type, temp_dir, repo_name): """ dest_path = os.path.join(temp_dir, repo_name) if source_type == "github": - try: - # Clone the GitHub repository - process = await self.clone_github_repo(source, dest_path) - return dest_path - except subprocess.CalledProcessError as e: - # Handle errors during the cloning process - raise Exception(f"Error cloning GitHub repository: {e.stderr.decode('utf-8')}") - except Exception as e: - # General error handling for unexpected issues - raise Exception(f"Unexpected error during GitHub cloning: {str(e)}") + repo_path = await self.download_and_extract_zip(source, temp_dir, repo_name) + return repo_path elif source_type == "zip": - # Handle ZIP extraction (Add your ZIP handling logic here) - pass + # Assume `repo_url_or_path` is a direct path to a ZIP file + repo_path = await self.extract_zip(source, temp_dir, repo_name) + return repo_path return dest_path - async def clone_github_repo(self, repo_url, dest_path): + async def download_and_extract_zip(self, zip_url, temp_dir, repo_name): + """ + Downloads and extracts a ZIP file from a URL. + """ + try: + async with aiohttp.ClientSession() as session: + async with session.get(zip_url) as response: + if response.status != 200: + raise Exception( + f"Failed to download ZIP file. Status code: {response.status}" + ) + + # Extract the ZIP file + zip_file_path = Path(temp_dir) / f"{repo_name}.zip" + with open(zip_file_path, "wb") as zip_file: + zip_data = await response.read() + zip_file.write(zip_data) + + # Extract to a directory + extraction_path = Path(temp_dir) / repo_name + try: + with zipfile.ZipFile(zip_file_path, "r") as zip_ref: + zip_ref.extractall(extraction_path) + except zipfile.BadZipFile as e: + raise Exception(f"Failed to extract ZIP file: {e}") + + return str(extraction_path) + except Exception as e: + raise + + async def extract_zip(self, zip_file_path, temp_dir, repo_name): """ - Clones a GitHub repository asynchronously. + Extracts a local ZIP file. + + Args: + zip_file_path (str): Path to the local ZIP file. + temp_dir (str): Temporary directory to store files. + repo_name (str): Repository identifier. + + Returns: + str: Path to the extracted contents. """ - loop = asyncio.get_event_loop() - await loop.run_in_executor(None, Repo.clone_from, repo_url, dest_path) + extraction_path = Path(temp_dir) / repo_name + with zipfile.ZipFile(zip_file_path, "r") as zip_ref: + zip_ref.extractall(extraction_path) + return str(extraction_path) def process_similarity_analysis(self, repo1_path, repo2_path): """ diff --git a/website/management/commands/owasp_project_upload.py b/website/management/commands/owasp_project_upload.py index e639cbdb9..ee11d3414 100644 --- a/website/management/commands/owasp_project_upload.py +++ b/website/management/commands/owasp_project_upload.py @@ -54,13 +54,14 @@ def handle(self, *args, **options): "Accept": "application/vnd.github.v3+json", } - # Check if OWASP organization exists - try: - org = Organization.objects.get(name__iexact="OWASP") + # Get or create OWASP organization + org, created = Organization.objects.get_or_create( + name__iexact="OWASP", defaults={"name": "OWASP"} + ) + if created: + self.stdout.write(self.style.SUCCESS(f"Created Organization: {org.name}")) + else: self.stdout.write(self.style.SUCCESS(f"Found Organization: {org.name}")) - except Organization.DoesNotExist: - self.stderr.write(self.style.ERROR("Organization 'OWASP' does not exist. Aborting.")) - return # Prompt user for confirmation confirm = ( @@ -76,7 +77,14 @@ def handle(self, *args, **options): try: with open(csv_file, newline="", encoding="utf-8") as f: reader = csv.DictReader(f) - required_fields = ["Name", "Tag", "License(s)", "Repo", "Website URL", "Code URL"] + required_fields = [ + "Name", + "Tag", + "License(s)", + "Repo", + "Website URL", + "Code URL", + ] for field in required_fields: if field not in reader.fieldnames: raise CommandError(f"Missing required field in CSV: {field}") @@ -270,15 +278,19 @@ def clean_github_url(url): is_main=False, stars=repo_info.get("stars", 0), forks=repo_info.get("forks", 0), - last_updated=parse_datetime(repo_info.get("last_updated")) - if repo_info.get("last_updated") - else None, + last_updated=( + parse_datetime(repo_info.get("last_updated")) + if repo_info.get("last_updated") + else None + ), watchers=repo_info.get("watchers", 0), primary_language=repo_info.get("primary_language", ""), license=repo_info.get("license", ""), - last_commit_date=parse_datetime(repo_info.get("last_commit_date")) - if repo_info.get("last_commit_date") - else None, + last_commit_date=( + parse_datetime(repo_info.get("last_commit_date")) + if repo_info.get("last_commit_date") + else None + ), network_count=repo_info.get("network_count", 0), subscribers_count=repo_info.get("subscribers_count", 0), size=repo_info.get("size", 0), @@ -290,9 +302,11 @@ def clean_github_url(url): contributor_count=repo_info.get("contributor_count", 0), commit_count=repo_info.get("commit_count", 0), release_name=repo_info.get("release_name", ""), - release_datetime=parse_datetime(repo_info.get("release_datetime")) - if repo_info.get("release_datetime") - else None, + release_datetime=( + parse_datetime(repo_info.get("release_datetime")) + if repo_info.get("release_datetime") + else None + ), ) except IntegrityError: self.stdout.write( @@ -376,17 +390,19 @@ def clean_github_url(url): is_main=idx == 1, stars=code_repo_info["stars"], forks=code_repo_info["forks"], - last_updated=parse_datetime(code_repo_info.get("last_updated")) - if code_repo_info.get("last_updated") - else None, + last_updated=( + parse_datetime(code_repo_info.get("last_updated")) + if code_repo_info.get("last_updated") + else None + ), watchers=code_repo_info["watchers"], primary_language=code_repo_info["primary_language"], license=code_repo_info["license"], - last_commit_date=parse_datetime( - code_repo_info.get("last_commit_date") - ) - if code_repo_info.get("last_commit_date") - else None, + last_commit_date=( + parse_datetime(code_repo_info.get("last_commit_date")) + if code_repo_info.get("last_commit_date") + else None + ), created=code_repo_info["created"], modified=code_repo_info["modified"], network_count=code_repo_info["network_count"], @@ -400,11 +416,11 @@ def clean_github_url(url): contributor_count=code_repo_info["contributor_count"], commit_count=code_repo_info["commit_count"], release_name=code_repo_info.get("release_name", ""), - release_datetime=parse_datetime( - code_repo_info.get("release_datetime") - ) - if code_repo_info.get("release_datetime") - else None, + release_datetime=( + parse_datetime(code_repo_info.get("release_datetime")) + if code_repo_info.get("release_datetime") + else None + ), ) except IntegrityError: self.stdout.write( @@ -429,7 +445,10 @@ def clean_github_url(url): # Handle contributors only for newly created repos if code_repo: code_contributors_data = self.fetch_contributors_data( - code_url, headers, delay_on_rate_limit, max_rate_limit_retries + code_url, + headers, + delay_on_rate_limit, + max_rate_limit_retries, ) if code_contributors_data: self.handle_contributors(code_repo, code_contributors_data) @@ -555,9 +574,9 @@ def api_get(url): "last_updated": repo_data.get("updated_at"), "watchers": repo_data.get("watchers_count", 0), "primary_language": repo_data.get("language", ""), - "license": repo_data.get("license", {}).get("name") - if repo_data.get("license") - else None, + "license": ( + repo_data.get("license", {}).get("name") if repo_data.get("license") else None + ), "last_commit_date": repo_data.get("pushed_at"), "created": repo_data.get("created_at", ""), "modified": repo_data.get("updated_at", ""), diff --git a/website/management/commands/sync_repo_contributors.py b/website/management/commands/sync_repo_contributors.py new file mode 100644 index 000000000..096639d37 --- /dev/null +++ b/website/management/commands/sync_repo_contributors.py @@ -0,0 +1,82 @@ +import logging +import time + +import requests +from django.conf import settings +from django.core.management.base import BaseCommand + +from website.models import Contributor, Repo + +logger = logging.getLogger(__name__) + + +class Command(BaseCommand): + help = "Synchronize all contributors for a repository" + + def add_arguments(self, parser): + parser.add_argument("--repo_id", type=int, help="Repository ID to sync") + + def handle(self, *args, **options): + repo_id = options.get("repo_id") + if not repo_id: + return + + repo = Repo.objects.get(id=repo_id) + owner_repo = repo.repo_url.rstrip("/").split("github.com/")[-1] + + headers = { + "Authorization": f"token {settings.GITHUB_TOKEN}", + "Accept": "application/vnd.github.v3+json", + } + + # Get all contributors with pagination + page = 1 + all_contributors = [] + + while True: + api_url = f"https://api.github.com/repos/{owner_repo}/contributors?anon=true&per_page=100&page={page}" + response = requests.get(api_url, headers=headers) + + if response.status_code == 403: + reset_time = int(response.headers.get("X-RateLimit-Reset", 0)) + wait_time = reset_time - int(time.time()) + if wait_time > 0: + logger.info(f"Rate limit hit, waiting {wait_time} seconds") + time.sleep(wait_time) + continue + + if response.status_code != 200: + break + + contributors_page = response.json() + if not contributors_page: + break + + all_contributors.extend(contributors_page) + page += 1 + + # Be nice to GitHub API + time.sleep(1) + + # Batch create/update contributors + for contrib_data in all_contributors: + github_id = contrib_data.get("id") + if not github_id: + # skip if 'id' is missing + continue + contributor, created = Contributor.objects.update_or_create( + github_id=github_id, + defaults={ + "name": contrib_data.get("login", "unknown"), + "github_url": contrib_data.get("html_url", ""), + "avatar_url": contrib_data.get("avatar_url", ""), + "contributions": contrib_data.get("contributions", 0), + "contributor_type": contrib_data.get("type", "User"), + }, + ) + repo.contributor.add(contributor) + + repo.contributor_count = len(all_contributors) + repo.save() + + logger.info(f"Synced {len(all_contributors)} contributors for {repo.name}") diff --git a/website/migrations/0173_challenge.py b/website/migrations/0173_challenge.py index ee96bb802..3241a983e 100644 --- a/website/migrations/0173_challenge.py +++ b/website/migrations/0173_challenge.py @@ -50,7 +50,7 @@ class Migration(migrations.Migration): ( "team_participants", models.ManyToManyField( - blank=True, related_name="team_challenges", to="website.company" + blank=True, related_name="team_challenges", to="website.organization" ), ), ], diff --git a/website/migrations/0177_project_project_visit_count.py b/website/migrations/0177_project_project_visit_count.py new file mode 100644 index 000000000..cc1bffff1 --- /dev/null +++ b/website/migrations/0177_project_project_visit_count.py @@ -0,0 +1,17 @@ +# Generated by Django 5.1.4 on 2025-01-01 20:15 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("website", "0176_repo_contributor_repo_contributor_count_and_more"), + ] + + operations = [ + migrations.AddField( + model_name="project", + name="project_visit_count", + field=models.IntegerField(default=0), + ), + ] diff --git a/website/migrations/0178_alter_ip_agent.py b/website/migrations/0178_alter_ip_agent.py new file mode 100644 index 000000000..b01cb53c1 --- /dev/null +++ b/website/migrations/0178_alter_ip_agent.py @@ -0,0 +1,17 @@ +# Generated by Django 5.1.4 on 2025-01-01 22:50 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("website", "0177_project_project_visit_count"), + ] + + operations = [ + migrations.AlterField( + model_name="ip", + name="agent", + field=models.TextField(blank=True, null=True), + ), + ] diff --git a/website/migrations/0179_contributorstats.py b/website/migrations/0179_contributorstats.py new file mode 100644 index 000000000..08fea502b --- /dev/null +++ b/website/migrations/0179_contributorstats.py @@ -0,0 +1,60 @@ +# Generated by Django 5.1.3 on 2025-01-06 13:17 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("website", "0178_alter_ip_agent"), + ] + + operations = [ + migrations.CreateModel( + name="ContributorStats", + fields=[ + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("date", models.DateField()), + ("commits", models.PositiveIntegerField(default=0)), + ("issues_opened", models.PositiveIntegerField(default=0)), + ("issues_closed", models.PositiveIntegerField(default=0)), + ("pull_requests", models.PositiveIntegerField(default=0)), + ("comments", models.PositiveIntegerField(default=0)), + ( + "granularity", + models.CharField( + choices=[("day", "Day"), ("month", "Month")], + default="day", + max_length=10, + ), + ), + ( + "contributor", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="stats", + to="website.contributor", + ), + ), + ( + "repo", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="stats", + to="website.repo", + ), + ), + ], + options={ + "unique_together": {("contributor", "repo", "date", "granularity")}, + }, + ), + ] diff --git a/website/migrations/0180_merge_0178_merge_20241229_1948_0179_contributorstats.py b/website/migrations/0180_merge_0178_merge_20241229_1948_0179_contributorstats.py new file mode 100644 index 000000000..76f1bac25 --- /dev/null +++ b/website/migrations/0180_merge_0178_merge_20241229_1948_0179_contributorstats.py @@ -0,0 +1,13 @@ +# Generated by Django 5.1.4 on 2025-01-08 09:04 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ("website", "0178_merge_20241229_1948"), + ("website", "0179_contributorstats"), + ] + + operations = [] diff --git a/website/models.py b/website/models.py index 804475f80..22557d1de 100644 --- a/website/models.py +++ b/website/models.py @@ -13,7 +13,7 @@ from colorthief import ColorThief from django.conf import settings from django.contrib.auth.models import User -from django.contrib.contenttypes.fields import GenericForeignKey +from django.contrib.contenttypes.fields import GenericForeignKey, GenericRelation from django.contrib.contenttypes.models import ContentType from django.core.cache import cache from django.core.exceptions import ValidationError @@ -77,7 +77,9 @@ class Integration(models.Model): blank=True, ) organization = models.ForeignKey( - "Organization", on_delete=models.CASCADE, related_name="organization_integrations" + "Organization", + on_delete=models.CASCADE, + related_name="organization_integrations", ) created_at = models.DateTimeField(auto_now_add=True) @@ -360,6 +362,7 @@ class Issue(models.Model): cve_id = models.CharField(max_length=16, null=True, blank=True) cve_score = models.DecimalField(max_digits=2, decimal_places=1, null=True, blank=True) tags = models.ManyToManyField(Tag, blank=True) + comments = GenericRelation("comments.Comment") def __unicode__(self): return self.description @@ -456,6 +459,7 @@ def delete_image_on_issue_delete(sender, instance, **kwargs): logger.error( f"Error deleting image from Google Cloud Storage: {blob_name} - {str(e)}" ) + else: @receiver(post_delete, sender=Issue) @@ -489,6 +493,7 @@ def delete_image_on_post_delete(sender, instance, **kwargs): logger.error( f"Error deleting image from Google Cloud Storage: {blob_name} - {str(e)}" ) + else: @receiver(post_delete, sender=IssueScreenshot) @@ -609,7 +614,11 @@ class UserProfile(models.Model): modified = models.DateTimeField(auto_now=True) visit_count = models.PositiveIntegerField(default=0) team = models.ForeignKey( - Organization, on_delete=models.SET_NULL, related_name="user_profiles", null=True, blank=True + Organization, + on_delete=models.SET_NULL, + related_name="user_profiles", + null=True, + blank=True, ) def check_team_membership(self): @@ -669,9 +678,9 @@ def update_streak_and_award_points(self, check_in_date=None): elif self.current_streak == 30: points_awarded += 50 reason = "30-day streak milestone achieved!" - elif self.current_streak == 90: + elif self.current_streak == 100: points_awarded += 150 - reason = "90-day streak milestone achieved!" + reason = "100-day streak milestone achieved!" elif self.current_streak == 180: points_awarded += 300 reason = "180-day streak milestone achieved!" @@ -703,7 +712,7 @@ def award_streak_badges(self): 7: "Weekly Streak", 15: "Half-Month Streak", 30: "Monthly Streak", - 90: "Three Month Streak", + 100: "100 Day Streak", 180: "Six Month Streak", 365: "Yearly Streak", } @@ -734,7 +743,7 @@ class IP(models.Model): user = models.CharField(max_length=150, null=True, blank=True) issuenumber = models.IntegerField(null=True, blank=True) created = models.DateTimeField(auto_now_add=True) - agent = models.CharField(max_length=255, null=True, blank=True) + agent = models.TextField(null=True, blank=True) count = models.BigIntegerField(default=1) path = models.CharField(max_length=255, null=True, blank=True) method = models.CharField(max_length=10, null=True, blank=True) @@ -892,7 +901,11 @@ def __str__(self): class Project(models.Model): organization = models.ForeignKey( - Organization, null=True, blank=True, related_name="projects", on_delete=models.CASCADE + Organization, + null=True, + blank=True, + related_name="projects", + on_delete=models.CASCADE, ) name = models.CharField(max_length=255) slug = models.SlugField(unique=True, blank=True) @@ -900,11 +913,14 @@ class Project(models.Model): url = models.URLField( unique=True, null=True, blank=True ) # Made url nullable in case of no website + project_visit_count = models.IntegerField(default=0) twitter = models.CharField(max_length=30, null=True, blank=True) facebook = models.URLField(null=True, blank=True) logo = models.ImageField(upload_to="project_logos", null=True, blank=True) created = models.DateTimeField(auto_now_add=True) # Standardized field name modified = models.DateTimeField(auto_now=True) # Standardized field name + # add languages + # add tags def save(self, *args, **kwargs): if not self.slug: @@ -923,17 +939,6 @@ def __str__(self): return self.name -# class ContributorStats(models.Model): -# username = models.CharField(max_length=255, unique=True) -# commits = models.IntegerField(default=0) -# issues_opened = models.IntegerField(default=0) -# issues_closed = models.IntegerField(default=0) -# prs = models.IntegerField(default=0) -# comments = models.IntegerField(default=0) -# assigned_issues = models.IntegerField(default=0) -# created = models.DateTimeField(auto_now_add=True) - - class Contribution(models.Model): CONTRIBUTION_TYPES = [ ("commit", "Commit"), @@ -1025,7 +1030,11 @@ class TimeLog(models.Model): ) # associate organization with sizzle organization = models.ForeignKey( - Organization, on_delete=models.CASCADE, related_name="time_logs", null=True, blank=True + Organization, + on_delete=models.CASCADE, + related_name="time_logs", + null=True, + blank=True, ) start_time = models.DateTimeField() end_time = models.DateTimeField(null=True, blank=True) @@ -1178,7 +1187,11 @@ class UserBadge(models.Model): user = models.ForeignKey(User, on_delete=models.CASCADE) badge = models.ForeignKey(Badge, on_delete=models.CASCADE) awarded_by = models.ForeignKey( - User, null=True, blank=True, related_name="awarded_badges", on_delete=models.SET_NULL + User, + null=True, + blank=True, + related_name="awarded_badges", + on_delete=models.SET_NULL, ) awarded_at = models.DateTimeField(auto_now_add=True) reason = models.TextField(blank=True, null=True) @@ -1195,6 +1208,7 @@ class Post(models.Model): created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) image = models.ImageField(upload_to="blog_posts") + comments = GenericRelation("comments.Comment") class Meta: db_table = "blog_post" @@ -1248,6 +1262,7 @@ class Repo(models.Model): tags = models.ManyToManyField("Tag", blank=True) last_updated = models.DateTimeField(null=True, blank=True) total_issues = models.IntegerField(default=0) + # rename this to repo_visit_count and make sure the github badge works with this project_visit_count = models.IntegerField(default=0) watchers = models.IntegerField(default=0) open_pull_requests = models.IntegerField(default=0) @@ -1297,6 +1312,35 @@ def __str__(self): return f"{self.project.name}/{self.name}" +class ContributorStats(models.Model): + contributor = models.ForeignKey(Contributor, on_delete=models.CASCADE, related_name="stats") + repo = models.ForeignKey(Repo, on_delete=models.CASCADE, related_name="stats") + + # This will represent either a specific day or the first day of a month. + date = models.DateField() + + # Store counts + commits = models.PositiveIntegerField(default=0) + issues_opened = models.PositiveIntegerField(default=0) + issues_closed = models.PositiveIntegerField(default=0) + pull_requests = models.PositiveIntegerField(default=0) + comments = models.PositiveIntegerField(default=0) + + # "day" for daily entries, "month" for monthly entries + granularity = models.CharField( + max_length=10, choices=[("day", "Day"), ("month", "Month")], default="day" + ) + + class Meta: + # You can't have two different stats for the same date+granularity + unique_together = ("contributor", "repo", "date", "granularity") + + def __str__(self): + return ( + f"{self.contributor.name} in {self.repo.name} " f"on {self.date} [{self.granularity}]" + ) + + class Challenge(models.Model): CHALLENGE_TYPE_CHOICES = [ ("single", "Single User"), diff --git a/website/serializers.py b/website/serializers.py index 74f64ca31..5e1129213 100644 --- a/website/serializers.py +++ b/website/serializers.py @@ -172,4 +172,9 @@ class ActivityLogSerializer(serializers.ModelSerializer): class Meta: model = ActivityLog fields = ["id", "user", "window_title", "url", "recorded_at", "created"] - read_only_fields = ["id", "user", "recorded_at", "created"] # Auto-filled fields + read_only_fields = [ + "id", + "user", + "recorded_at", + "created", + ] # Auto-filled fields diff --git a/website/templates/account/login.html b/website/templates/account/login.html index 3886db164..d1cca3596 100644 --- a/website/templates/account/login.html +++ b/website/templates/account/login.html @@ -106,50 +106,40 @@

{% trans "Account Login" % {% endfor %} {% endif %} -
+
diff --git a/website/templates/blog/post_details.html b/website/templates/blog/post_details.html index 5cd2399ad..5c2e01342 100644 --- a/website/templates/blog/post_details.html +++ b/website/templates/blog/post_details.html @@ -119,5 +119,6 @@

{{ post.title }}

Delete Post {% endif %} +
{% include "../comments2.html" %}
{% endblock content %} diff --git a/website/templates/comments2.html b/website/templates/comments2.html index 730507ad8..5211a942c 100644 --- a/website/templates/comments2.html +++ b/website/templates/comments2.html @@ -9,6 +9,10 @@

Comments ({{ all_commen
+
+
+ +
+
+ + +
+
+ + +
+
+ +
+

Repositories

+
+ +
+
+
+ + +
+
+ + +
+
+
+
+ + +
+ + +
+ + +
+ + + + + + + + {% endblock content %} diff --git a/website/templates/projects/repo_detail.html b/website/templates/projects/repo_detail.html new file mode 100644 index 000000000..83ed4ab96 --- /dev/null +++ b/website/templates/projects/repo_detail.html @@ -0,0 +1,763 @@ +{% extends "base.html" %} +{% load humanize %} +{% load static %} +{% block title %} + {{ repo.name }} - Repo Details +{% endblock title %} +{% block content %} + {% include "includes/sidenav.html" %} + + +
+ +
+
+
+
+ {% if repo.logo_url %} + {{ repo.name }} logo + {% endif %} +
+

{{ repo.name }}

+
+ + + + + + Back to {{ repo.project.name }} + + {% if repo.project.organization %} + | +
organization : {{ repo.project.organization.name }}
+ {% endif %} +
+
+
+

{{ repo.description|default:"No description available." }}

+
+ +
+ {% if repo.homepage_url %} + + + + + Visit Homepage + + {% endif %} + + + + + View on GitHub + +
+
+ +
+
+ {% if repo.is_main %} + + + + + Main Repository + + {% elif repo.is_wiki %} + + + + + Wiki Repository + + {% else %} + + + + + Standard Repository + + {% endif %} +
+ Tags: + {% for tag in repo.tags.all %} + {{ tag.name }} + {% endfor %} +
+ +
+
+
+
+
Created {{ repo.created|date:"M d, Y" }}
+
Updated {{ repo.last_updated|naturaltime }}
+
+
+ +
+
+
+ Stars + + + +
+

{{ repo.stars|intcomma }}

+
+
+
+ Forks + + + +
+

{{ repo.forks|intcomma }}

+
+
+
+ Watchers + + + + +
+

{{ repo.watchers|intcomma }}

+
+
+
+ Network + + + +
+

{{ repo.network_count|intcomma }}

+
+
+
+ Subscribers + + + +
+

{{ repo.subscribers_count|intcomma }}

+
+
+
+ +
+ +
+ +
+
+
+

+ + + + Activity Metrics +

+
+ +
+
+
+ +
+
+
+
+
+

Issues

+ + + + + +
+
+
+ Open + {{ repo.open_issues|intcomma }} +
+
+ Closed + {{ repo.closed_issues|intcomma }} +
+
+
+
+ Total + {{ repo.total_issues|intcomma }} +
+
+
+
+ +
+
+
+
+
+

Pull Requests

+ + + + + +
+
+
+
+ {{ repo.open_pull_requests|intcomma }} +
+
Open PRs
+
+
+
+
+ +
+
+
+
+
+

Commits

+ + + + + +
+
+
+
{{ repo.commit_count|intcomma }}
+
Total Commits
+
+
+ + + + Last: + {{ repo.last_commit_date|date:"M d, Y" }} +
+
+
+
+
+
+
+ +
+
+
+

+ + + + Community +

+
+ +
+
+
+ +
+

Top Contributors

+ + {{ repo.contributor_count|intcomma }} total contributors + +
+ +
+ {% for contributor in top_contributors %} +
+ {{ contributor.name }} +
+
+ {{ contributor.name }} + {% if contributor.verified %}{% endif %} +
+
{{ contributor.contributions|intcomma }} commits
+
+ + + + + +
+ {% endfor %} +
+ + {% if repo.contributor_count > 6 %} +
+
+
+ {% for contributor in repo.contributor.all|dictsortreversed:"contributions"|slice:"6:11" %} + {{ contributor.name }} + {% endfor %} + {% if repo.contributor_count > 11 %} +
+ +{{ repo.contributor_count|add:"-11" }} +
+ {% endif %} +
+ + View all contributors + + + + +
+
+ {% endif %} +
+
+
+ +
+
+
+

+ + + + Technical Overview +

+
+ +
+
+
+ +
+

+ + + + Language & Stack +

+
+
+ Primary Language: + + {{ repo.primary_language|default:"Not specified" }} + +
+
+ Repository Size: + {{ repo.size|filesizeformat }} +
+
+ License: +
{{ repo.license|default:"Not specified" }}
+
+
+
+ +
+

+ + + + Latest Release +

+ {% if repo.release_name or repo.release_datetime %} +
+ {% if repo.release_name %} +
+ Version + {{ repo.release_name }} +
+ {% endif %} + {% if repo.release_datetime %} +
+ Release Date + + {{ repo.release_datetime|date:"M d, Y" }} + +
+ {% endif %} +
+ Last Commit + + {{ repo.last_commit_date|date:"M d, Y" }} + +
+
+ {% else %} +
No release information available
+ {% endif %} +
+
+
+
+
+ + +
+
+ +{% endblock content %} +{% block after_js %} + +{% endblock after_js %} diff --git a/website/templates/similarity.html b/website/templates/similarity.html index 3eba92554..794e5451f 100644 --- a/website/templates/similarity.html +++ b/website/templates/similarity.html @@ -127,6 +127,14 @@

Similarity Check

+
+ +
@@ -144,6 +152,14 @@

Similarity Check

+
+ +
@@ -237,8 +253,12 @@

Results

const repo1 = document.getElementById("repo1").value; const type1 = document.getElementById("type1").value; + const branch1 = document.getElementById("branch1").value; + const repo2 = document.getElementById("repo2").value; const type2 = document.getElementById("type2").value; + const branch2 = document.getElementById("branch2").value; + // Validate input if (!repo1 || !repo2) { @@ -257,7 +277,10 @@

Results

repo1: repo1, type1: type1, repo2: repo2, - type2: type2 + type2: type2, + branch1: branch1, + branch2: branch2 + })); } catch (error) { diff --git a/website/templates/status_page.html b/website/templates/status_page.html index 25f06a904..90b962f0c 100644 --- a/website/templates/status_page.html +++ b/website/templates/status_page.html @@ -52,14 +52,14 @@ .status-dot.red { background-color: #dc3545; } - .memory-info, .memory-consumers { + .memory-info, .memory-consumers, .memory-by-module { margin-top: 10px; } - .memory-consumers ul { + .memory-consumers ul, .memory-by-module ul { list-style-type: none; padding: 0; } - .memory-consumers li { + .memory-consumers li, .memory-by-module li { margin-bottom: 5px; } @@ -69,8 +69,11 @@

Bitcoin Node

-
- {% if status.bitcoin %} +
+
+ {% if status.bitcoin == None %} + Status Unknown + {% elif status.bitcoin %} Operational

Block Height: {{ status.bitcoin_block }}

Bitcoin Node

SendGrid API

-
- {% if status.sendgrid %} +
+
+ {% if status.sendgrid == None %} + Status Unknown + {% elif status.sendgrid %} Operational {% else %} Not Operational @@ -97,8 +103,10 @@

SendGrid API

GitHub API

-
- {% if status.github %} +
+ {% if status.github == None %} + Status Unknown + {% elif status.github %} Operational {% else %} Not Operational @@ -147,5 +155,45 @@

Top Memory Consumers

+ +
+

Memory Usage by Module

+
+
    + {% for module, memory in status.memory_by_module %} +
  • + {{ module }}: {{ memory|filesizeformat }} +
  • + {% endfor %} +
+
+
+
+

Database Connection Count

+

{{ status.db_connection_count }}

+
+
+

Redis Stats

+
    + {% for key, value in status.redis_stats.items %} +
  • + {{ key }}: {{ value }} +
  • + {% endfor %} +
+
+
+

OpenAI API

+
+
+ {% if status.openai == None %} + Status Unknown + {% elif status.openai %} + Operational + {% else %} + Not Operational + {% endif %} +
+
{% endblock content %} diff --git a/website/utils.py b/website/utils.py index 3f7964cd7..46f0ee309 100644 --- a/website/utils.py +++ b/website/utils.py @@ -7,7 +7,8 @@ from urllib.parse import urlparse, urlsplit, urlunparse import numpy as np -import openai + +# import openai import requests from bs4 import BeautifulSoup from django.core.exceptions import ValidationError @@ -17,7 +18,7 @@ from .models import PRAnalysisReport -openai.api_key = os.getenv("OPENAI_API_KEY") +# openai.api_key = os.getenv("OPENAI_API_KEY") GITHUB_API_TOKEN = os.getenv("GITHUB_ACCESS_TOKEN") WHITELISTED_IMAGE_TYPES = { @@ -469,3 +470,12 @@ def compare_model_fields(model1, model2): "field_comparison_details": field_comparison_details, "overall_field_similarity": round(overall_field_similarity, 2), } + + +def git_url_to_zip_url(git_url, branch="master"): + if git_url.endswith(".git"): + base_url = git_url[:-4] + zip_url = f"{base_url}/archive/refs/heads/{branch}.zip" + return zip_url + else: + raise ValueError("Invalid .git URL provided") diff --git a/website/views/blog.py b/website/views/blog.py index 09477e463..304c3d1bc 100644 --- a/website/views/blog.py +++ b/website/views/blog.py @@ -1,5 +1,6 @@ import markdown from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin +from django.contrib.contenttypes.models import ContentType from django.utils.text import slugify from django.views import generic @@ -22,6 +23,12 @@ def get_object(self): post.content = markdown.markdown(post.content) return post + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + context["content_type"] = ContentType.objects.get_for_model(Post).model + context["all_comment"] = self.object.comments.all() + return context + class PostCreateView(LoginRequiredMixin, generic.CreateView): model = Post diff --git a/website/views/core.py b/website/views/core.py index 5b1267a8f..02f8db300 100644 --- a/website/views/core.py +++ b/website/views/core.py @@ -3,7 +3,6 @@ import subprocess import tracemalloc import urllib -from datetime import datetime, timezone import psutil import requests @@ -21,6 +20,7 @@ from django.core.exceptions import FieldError from django.core.files.base import ContentFile from django.core.files.storage import default_storage +from django.db import connection from django.db.models import Count, Q, Sum from django.db.models.functions import TruncDate from django.http import Http404, HttpResponse, JsonResponse @@ -30,17 +30,11 @@ from django.views.decorators.csrf import csrf_exempt from django.views.decorators.http import require_GET from django.views.generic import TemplateView, View -from requests.auth import HTTPBasicAuth -from rest_framework import status -from rest_framework.decorators import api_view -from rest_framework.response import Response -from sendgrid import SendGridAPIClient +from django_redis import get_redis_connection from blt import settings -from website.bot import conversation_chain, is_api_key_valid, load_vector_store from website.models import ( Badge, - ChatBotLog, Domain, Issue, PRAnalysisReport, @@ -56,108 +50,221 @@ save_analysis_report, ) -vector_store = None -DAILY_REQUEST_LIMIT = 10 +# from website.bot import conversation_chain, is_api_key_valid, load_vector_store -def check_status(request): - status = cache.get("service_status") +# ---------------------------------------------------------------------------------- +# 1) Helper function to measure memory usage by module using tracemalloc +# ---------------------------------------------------------------------------------- - if not status: - tracemalloc.start() # Start memory profiling - status = { - "bitcoin": False, - "bitcoin_block": None, - "sendgrid": False, - "github": False, - "memory_info": psutil.virtual_memory()._asdict(), - "top_memory_consumers": [], - "memory_profiling": {}, - } +def memory_usage_by_module(limit=1000): + """ + Returns a list of (filename, size_in_bytes) for the top + `limit` files by allocated memory, using tracemalloc. + """ + # tracemalloc.start() + try: + snapshot = tracemalloc.take_snapshot() + except Exception as e: + print("Error taking memory snapshot: ", e) + return [] + print("Memory snapshot taken. and it is: ", snapshot) - bitcoin_rpc_user = os.getenv("BITCOIN_RPC_USER") - bitcoin_rpc_password = os.getenv("BITCOIN_RPC_PASSWORD") - bitcoin_rpc_host = os.getenv("BITCOIN_RPC_HOST", "127.0.0.1") - bitcoin_rpc_port = os.getenv("BITCOIN_RPC_PORT", "8332") + stats = snapshot.statistics("traceback") + for stat in stats[:10]: + print(stat.traceback.format()) + print(f"Memory: {stat.size / 1024:.2f} KB") - try: - response = requests.post( - f"http://{bitcoin_rpc_host}:{bitcoin_rpc_port}", - json={ - "jsonrpc": "1.0", - "id": "curltest", - "method": "getblockchaininfo", - "params": [], - }, - auth=HTTPBasicAuth(bitcoin_rpc_user, bitcoin_rpc_password), - timeout=2, # Set a timeout to avoid hanging - ) - if response.status_code == 200: - data = response.json().get("result", {}) - status["bitcoin"] = True - status["bitcoin_block"] = data.get("blocks", None) - except requests.exceptions.RequestException as e: - print(f"Bitcoin Core Node Error: {e}") + # Group memory usage by filename + stats = snapshot.statistics("filename") + module_usage = {} + + for stat in stats: + print("stat is: ", stat) + if stat.traceback: + filename = stat.traceback[0].filename + # Accumulate memory usage + module_usage[filename] = module_usage.get(filename, 0) + stat.size + + # Sort by highest usage + sorted_by_usage = sorted(module_usage.items(), key=lambda x: x[1], reverse=True)[:limit] + + tracemalloc.stop() + return sorted_by_usage + + +# ---------------------------------------------------------------------------------- +# 2) Example: Calculate your service/application status +# ---------------------------------------------------------------------------------- + +DAILY_REQUEST_LIMIT = 10 +vector_store = None - try: - sg = SendGridAPIClient(os.getenv("SENDGRID_PASSWORD")) - response = sg.client.api_keys._(sg.api_key).get() - if response.status_code == 200: - status["sendgrid"] = True - except Exception as e: - print(f"SendGrid Error: {e}") - github_token = os.getenv("GITHUB_ACCESS_TOKEN") +def check_status(request): + """ + Status check function with configurable components. + Enable/disable specific checks using the CONFIG constants. + """ + # Configuration flags + CHECK_BITCOIN = False + CHECK_SENDGRID = False + CHECK_GITHUB = False + CHECK_OPENAI = False + CHECK_MEMORY = True + CHECK_DATABASE = False + CHECK_REDIS = False + CACHE_TIMEOUT = 60 + + status_data = cache.get("service_status") + + if not status_data: + status_data = { + "bitcoin": None if not CHECK_BITCOIN else False, + "bitcoin_block": None, + "sendgrid": None if not CHECK_SENDGRID else False, + "github": None if not CHECK_GITHUB else False, + "openai": None if not CHECK_OPENAI else False, + "db_connection_count": None if not CHECK_DATABASE else 0, + "redis_stats": {} if not CHECK_REDIS else {}, + } - if not github_token: - print( - "GitHub Access Token not found. Please set the GITHUB_ACCESS_TOKEN environment variable." + if CHECK_MEMORY: + status_data.update( + { + "memory_info": psutil.virtual_memory()._asdict(), + "top_memory_consumers": [], + "memory_profiling": {}, + "memory_by_module": [], + } ) - status["github"] = False - else: + + # Bitcoin RPC check + if CHECK_BITCOIN: + bitcoin_rpc_user = os.getenv("BITCOIN_RPC_USER") + bitcoin_rpc_password = os.getenv("BITCOIN_RPC_PASSWORD") + bitcoin_rpc_host = os.getenv("BITCOIN_RPC_HOST", "127.0.0.1") + bitcoin_rpc_port = os.getenv("BITCOIN_RPC_PORT", "8332") + try: - headers = {"Authorization": f"token {github_token}"} - response = requests.get( - "https://api.github.com/user/repos", headers=headers, timeout=5 + print("Checking Bitcoin RPC...") + response = requests.post( + f"http://{bitcoin_rpc_host}:{bitcoin_rpc_port}", + json={ + "jsonrpc": "1.0", + "id": "curltest", + "method": "getblockchaininfo", + "params": [], + }, + auth=(bitcoin_rpc_user, bitcoin_rpc_password), + timeout=5, ) - if response.status_code == 200: - status["github"] = True - else: - status["github"] = False - print( - f"GitHub API token check failed with status code {response.status_code}: {response.json().get('message', 'No message provided')}" - ) - + status_data["bitcoin"] = True + status_data["bitcoin_block"] = response.json().get("result", {}).get("blocks") except requests.exceptions.RequestException as e: - status["github"] = False - print(f"GitHub API Error: {e}") - - # Get the top 5 processes by memory usage - for proc in psutil.process_iter(["pid", "name", "memory_info"]): + print(f"Bitcoin RPC Error: {e}") + + # SendGrid API check + if CHECK_SENDGRID: + sendgrid_api_key = os.getenv("SENDGRID_API_KEY") + if sendgrid_api_key: + try: + print("Checking SendGrid API...") + response = requests.get( + "https://api.sendgrid.com/v3/user/account", + headers={"Authorization": f"Bearer {sendgrid_api_key}"}, + timeout=5, + ) + status_data["sendgrid"] = response.status_code == 200 + except requests.exceptions.RequestException as e: + print(f"SendGrid API Error: {e}") + + # GitHub API check + if CHECK_GITHUB: + github_token = os.getenv("GITHUB_TOKEN") + if github_token: + try: + print("Checking GitHub API...") + response = requests.get( + "https://api.github.com/user/repos", + headers={"Authorization": f"token {github_token}"}, + timeout=5, + ) + status_data["github"] = response.status_code == 200 + except requests.exceptions.RequestException as e: + print(f"GitHub API Error: {e}") + + # OpenAI API check + if CHECK_OPENAI: + openai_api_key = os.getenv("OPENAI_API_KEY") + if openai_api_key: + try: + print("Checking OpenAI API...") + response = requests.get( + "https://api.openai.com/v1/models", + headers={"Authorization": f"Bearer {openai_api_key}"}, + timeout=5, + ) + status_data["openai"] = response.status_code == 200 + except requests.exceptions.RequestException as e: + print(f"OpenAI API Error: {e}") + + # Memory usage checks + if CHECK_MEMORY: + print("Getting memory usage information...") + tracemalloc.start() + + # Get top memory consumers + for proc in psutil.process_iter(["pid", "name", "memory_info"]): + try: + proc_info = proc.info + proc_info["memory_info"] = proc_info["memory_info"]._asdict() + status_data["top_memory_consumers"].append(proc_info) + except ( + psutil.NoSuchProcess, + psutil.AccessDenied, + psutil.ZombieProcess, + ): + pass + + status_data["top_memory_consumers"] = sorted( + status_data["top_memory_consumers"], + key=lambda x: x["memory_info"]["rss"], + reverse=True, + )[:5] + + # Memory profiling info + current, peak = tracemalloc.get_traced_memory() + status_data["memory_profiling"]["current"] = current + status_data["memory_profiling"]["peak"] = peak + tracemalloc.stop() + + # Database connection check + if CHECK_DATABASE: + print("Getting database connection count...") + if ( + settings.DATABASES.get("default", {}).get("ENGINE") + == "django.db.backends.postgresql" + ): + with connection.cursor() as cursor: + cursor.execute("SELECT COUNT(*) FROM pg_stat_activity WHERE state = 'active'") + status_data["db_connection_count"] = cursor.fetchone()[0] + + # Redis stats + if CHECK_REDIS: + print("Getting Redis stats...") try: - proc_info = proc.info - proc_info["memory_info"] = proc_info["memory_info"]._asdict() - status["top_memory_consumers"].append(proc_info) - except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess): - pass - - status["top_memory_consumers"] = sorted( - status["top_memory_consumers"], - key=lambda x: x["memory_info"]["rss"], - reverse=True, - )[:5] - - # Add memory profiling information - current, peak = tracemalloc.get_traced_memory() - status["memory_profiling"]["current"] = current - status["memory_profiling"]["peak"] = peak - tracemalloc.stop() + redis_client = get_redis_connection("default") + status_data["redis_stats"] = redis_client.info() + except Exception as e: + print(f"Redis error or not supported: {e}") - cache.set("service_status", status, timeout=60) + # Cache the results + cache.set("service_status", status_data, timeout=CACHE_TIMEOUT) - return render(request, "status_page.html", {"status": status}) + return render(request, "status_page.html", {"status": status_data}) def github_callback(request): @@ -246,91 +353,90 @@ def search(request, template="search.html"): return render(request, template, context) -@api_view(["POST"]) -def chatbot_conversation(request): - try: - today = datetime.now(timezone.utc).date() - rate_limit_key = f"global_daily_requests_{today}" - request_count = cache.get(rate_limit_key, 0) - - if request_count >= DAILY_REQUEST_LIMIT: - return Response( - {"error": "Daily request limit exceeded."}, - status=status.HTTP_429_TOO_MANY_REQUESTS, - ) - - question = request.data.get("question", "") - if not question: - return Response({"error": "Invalid question"}, status=status.HTTP_400_BAD_REQUEST) - check_api = is_api_key_valid(os.getenv("OPENAI_API_KEY")) - if not check_api: - ChatBotLog.objects.create(question=question, answer="Error: Invalid API Key") - return Response({"error": "Invalid API Key"}, status=status.HTTP_400_BAD_REQUEST) - - if not question or not isinstance(question, str): - ChatBotLog.objects.create(question=question, answer="Error: Invalid question") - return Response({"error": "Invalid question"}, status=status.HTTP_400_BAD_REQUEST) - - global vector_store - if not vector_store: - try: - vector_store = load_vector_store() - except FileNotFoundError as e: - ChatBotLog.objects.create( - question=question, answer="Error: Vector store not found {e}" - ) - return Response( - {"error": "Vector store not found"}, - status=status.HTTP_400_BAD_REQUEST, - ) - except Exception as e: - ChatBotLog.objects.create(question=question, answer=f"Error: {str(e)}") - return Response( - {"error": "Error loading vector store"}, - status=status.HTTP_500_INTERNAL_SERVER_ERROR, - ) - finally: - if not vector_store: - ChatBotLog.objects.create( - question=question, answer="Error: Vector store not loaded" - ) - return Response( - {"error": "Vector store not loaded"}, - status=status.HTTP_500_INTERNAL_SERVER_ERROR, - ) - - if question.lower() == "exit": - if "buffer" in request.session: - del request.session["buffer"] - return Response({"answer": "Conversation memory cleared."}, status=status.HTTP_200_OK) - - crc, memory = conversation_chain(vector_store) - if "buffer" in request.session: - memory.buffer = request.session["buffer"] - - try: - response = crc.invoke({"question": question}) - except Exception as e: - ChatBotLog.objects.create(question=question, answer=f"Error: {str(e)}") - return Response( - {"error": "An internal error has occurred."}, - status=status.HTTP_500_INTERNAL_SERVER_ERROR, - ) - cache.set(rate_limit_key, request_count + 1, timeout=86400) # Timeout set to one day - request.session["buffer"] = memory.buffer - - ChatBotLog.objects.create(question=question, answer=response["answer"]) - - return Response({"answer": response["answer"]}, status=status.HTTP_200_OK) - - except Exception as e: - ChatBotLog.objects.create( - question=request.data.get("question", ""), answer=f"Error: {str(e)}" - ) - return Response( - {"error": "An internal error has occurred."}, - status=status.HTTP_500_INTERNAL_SERVER_ERROR, - ) +# @api_view(["POST"]) +# def chatbot_conversation(request): +# try: +# today = datetime.now(timezone.utc).date() +# rate_limit_key = f"global_daily_requests_{today}" +# request_count = cache.get(rate_limit_key, 0) + +# if request_count >= DAILY_REQUEST_LIMIT: +# return Response( +# {"error": "Daily request limit exceeded."}, +# status=status.HTTP_429_TOO_MANY_REQUESTS, +# ) + +# question = request.data.get("question", "") +# if not question: +# return Response({"error": "Invalid question"}, status=status.HTTP_400_BAD_REQUEST) +# check_api = is_api_key_valid(os.getenv("OPENAI_API_KEY")) +# if not check_api: +# ChatBotLog.objects.create(question=question, answer="Error: Invalid API Key") +# return Response({"error": "Invalid API Key"}, status=status.HTTP_400_BAD_REQUEST) + +# if not question or not isinstance(question, str): +# ChatBotLog.objects.create(question=question, answer="Error: Invalid question") +# return Response({"error": "Invalid question"}, status=status.HTTP_400_BAD_REQUEST) + +# global vector_store +# if not vector_store: +# try: +# vector_store = load_vector_store() +# except FileNotFoundError as e: +# ChatBotLog.objects.create( +# question=question, answer="Error: Vector store not found {e}" +# ) +# return Response( +# {"error": "Vector store not found"}, +# status=status.HTTP_400_BAD_REQUEST, +# ) +# except Exception as e: +# ChatBotLog.objects.create(question=question, answer=f"Error: {str(e)}") +# return Response( +# {"error": "Error loading vector store"}, +# status=status.HTTP_500_INTERNAL_SERVER_ERROR, +# ) +# finally: +# if not vector_store: +# ChatBotLog.objects.create( +# question=question, answer="Error: Vector store not loaded" +# ) +# return Response( +# {"error": "Vector store not loaded"}, +# status=status.HTTP_500_INTERNAL_SERVER_ERROR, +# ) + +# if question.lower() == "exit": +# if "buffer" in request.session: +# del request.session["buffer"] +# return Response({"answer": "Conversation memory cleared."}, status=status.HTTP_200_OK) + +# crc, memory = conversation_chain(vector_store) +# if "buffer" in request.session: +# memory.buffer = request.session["buffer"] + +# try: +# response = crc.invoke({"question": question}) +# except Exception as e: +# ChatBotLog.objects.create(question=question, answer=f"Error: {str(e)}") +# return Response( +# {"error": "An internal error has occurred."}, +# status=status.HTTP_500_INTERNAL_SERVER_ERROR, +# ) +# cache.set(rate_limit_key, request_count + 1, timeout=86400) # Timeout set to one day +# request.session["buffer"] = memory.buffer + +# ChatBotLog.objects.create(question=question, answer=response["answer"]) +# return Response({"answer": response["answer"]}, status=status.HTTP_200_OK) + +# except Exception as e: +# ChatBotLog.objects.create( +# question=request.data.get("question", ""), answer=f"Error: {str(e)}" +# ) +# return Response( +# {"error": "An internal error has occurred."}, +# status=status.HTTP_500_INTERNAL_SERVER_ERROR, +# ) @login_required @@ -496,7 +602,7 @@ def dispatch(self, request, *args, **kwargs): def post(self, request, *args, **kwargs): data = request.FILES.get("image") result = default_storage.save( - "uploads\/" + self.kwargs["hash"] + ".png", ContentFile(data.read()) + "uploads/" + self.kwargs["hash"] + ".png", ContentFile(data.read()) ) return JsonResponse({"status": result}) @@ -505,10 +611,9 @@ class StatsDetailView(TemplateView): template_name = "stats.html" def get_historical_counts(self, model): - # Map models to their date fields date_field_map = { "Issue": "created", - "UserProfile": "created", # From user creation + "UserProfile": "created", "Comment": "created_date", "Hunt": "created", "Domain": "created", @@ -531,7 +636,6 @@ def get_historical_counts(self, model): "BaconToken": "date_awarded", "IP": "created", "ChatBotLog": "created", - # Add other models as needed } date_field = date_field_map.get(model.__name__, "created") @@ -539,7 +643,6 @@ def get_historical_counts(self, model): counts = [] try: - # Annotate and count by truncated date date_counts = ( model.objects.annotate(date=TruncDate(date_field)) .values("date") @@ -550,7 +653,6 @@ def get_historical_counts(self, model): dates.append(entry["date"].strftime("%Y-%m-%d")) counts.append(entry["count"]) except FieldError: - # If the date field doesn't exist, return empty lists return [], [] return dates, counts @@ -592,8 +694,8 @@ def get_context_data(self, *args, **kwargs): for model in apps.get_models(): if model._meta.abstract or model._meta.proxy: continue - model_name = model.__name__ + try: dates, counts = self.get_historical_counts(model) trend = counts[-1] - counts[-2] if len(counts) >= 2 else 0 @@ -604,13 +706,12 @@ def get_context_data(self, *args, **kwargs): "label": model_name, "count": total_count, "icon": known_icons.get(model_name, "fas fa-database"), - "history": json.dumps(counts), # Serialize counts to JSON - "dates": json.dumps(dates), # Serialize dates to JSON + "history": json.dumps(counts), + "dates": json.dumps(dates), "trend": trend, } ) - except Exception as e: - # Optionally log the exception + except Exception: continue context["stats"] = sorted(stats_data, key=lambda x: x["count"], reverse=True) @@ -646,7 +747,6 @@ def get_bch_balance(address): return None bch_address = "bitcoincash:qr5yccf7j4dpjekyz3vpawgaarl352n7yv5d5mtzzc" - balance = get_bch_balance(bch_address) if balance is not None: print(f"Balance of {bch_address}: {balance} BCH") @@ -667,9 +767,6 @@ def robots_txt(request): return HttpResponse("\n".join(lines), content_type="text/plain") -import os - - def get_last_commit_date(): try: return ( @@ -697,25 +794,21 @@ def submit_roadmap_pr(request): owner, repo = pr_parts[3], pr_parts[4] pr_number, issue_number = pr_parts[-1], issue_parts[-1] - print(pr_parts) - print(issue_parts) - - print(f"rrepo: {repo}") - print(f"pr_number: {pr_number}") - pr_data = fetch_github_data(owner, repo, "pulls", pr_number) roadmap_data = fetch_github_data(owner, repo, "issues", issue_number) if "error" in pr_data or "error" in roadmap_data: return JsonResponse( { - "error": f"Failed to fetch PR or roadmap data: {pr_data.get('error', 'Unknown error')}" + "error": ( + f"Failed to fetch PR or roadmap data: " + f"{pr_data.get('error', 'Unknown error')}" + ) }, status=500, ) analysis = analyze_pr_content(pr_data, roadmap_data) - save_analysis_report(pr_link, issue_link, analysis) return JsonResponse({"message": "PR submitted successfully"}) diff --git a/website/views/issue.py b/website/views/issue.py index 3f8474b0b..a7298033c 100644 --- a/website/views/issue.py +++ b/website/views/issue.py @@ -15,6 +15,7 @@ from django.contrib import messages from django.contrib.auth.decorators import login_required from django.contrib.auth.models import User +from django.contrib.contenttypes.models import ContentType from django.core import serializers from django.core.files import File from django.core.files.base import ContentFile @@ -158,7 +159,10 @@ def create_github_issue(request, id): return JsonResponse({"status": "Failed", "status_reason": "GitHub Access Token is missing"}) if issue.github_url: return JsonResponse( - {"status": "Failed", "status_reason": "GitHub Issue Exists at " + issue.github_url} + { + "status": "Failed", + "status_reason": "GitHub Issue Exists at " + issue.github_url, + } ) if issue.domain.github: screenshot_text = "" @@ -194,7 +198,10 @@ def create_github_issue(request, id): return JsonResponse({"status": "ok", "github_url": issue.github_url}) else: return JsonResponse( - {"status": "Failed", "status_reason": f"Issue with Github: {response.reason}"} + { + "status": "Failed", + "status_reason": f"Issue with Github: {response.reason}", + } ) except Exception as e: send_mail( @@ -207,7 +214,10 @@ def create_github_issue(request, id): return JsonResponse({"status": "Failed", "status_reason": f"Failed: error is {e}"}) else: return JsonResponse( - {"status": "Failed", "status_reason": "No Github URL for this domain, please add it."} + { + "status": "Failed", + "status_reason": "No Github URL for this domain, please add it.", + } ) @@ -329,7 +339,8 @@ def newhome(request, template="new_home.html"): current_time = now() leaderboard = ( User.objects.filter( - points__created__month=current_time.month, points__created__year=current_time.year + points__created__month=current_time.month, + points__created__year=current_time.year, ) .annotate(total_points=Sum("points__score")) .order_by("-total_points") @@ -576,10 +587,10 @@ def submit_pr(request): class IssueBaseCreate(object): def form_valid(self, form): - print( - "processing form_valid IssueBaseCreate for ip address: ", - get_client_ip(self.request), - ) + # print( + # "processing form_valid IssueBaseCreate for ip address: ", + # get_client_ip(self.request), + # ) score = 3 obj = form.save(commit=False) obj.user = self.request.user @@ -610,7 +621,7 @@ def form_valid(self, form): p = Points.objects.create(user=self.request.user, issue=obj, score=score) def process_issue(self, user, obj, created, domain, tokenauth=False, score=3): - print("processing process_issue for ip address: ", get_client_ip(self.request)) + # print("processing process_issue for ip address: ", get_client_ip(self.request)) p = Points.objects.create(user=user, issue=obj, score=score, reason="Issue reported") messages.success(self.request, "Bug added ! +" + str(score)) try: @@ -750,7 +761,7 @@ class IssueCreate(IssueBaseCreate, CreateView): template_name = "report.html" def get_initial(self): - print("processing post for ip address: ", get_client_ip(self.request)) + # print("processing post for ip address: ", get_client_ip(self.request)) try: json_data = json.loads(self.request.body) if not self.request.GET._mutable: @@ -806,7 +817,7 @@ def get_initial(self): return initial def post(self, request, *args, **kwargs): - print("processing post for ip address: ", get_client_ip(request)) + # print("processing post for ip address: ", get_client_ip(request)) url = request.POST.get("url").replace("www.", "").replace("https://", "") request.POST._mutable = True @@ -871,10 +882,10 @@ def post(self, request, *args, **kwargs): return super().post(request, *args, **kwargs) def form_valid(self, form): - print( - "processing form_valid in IssueCreate for ip address: ", - get_client_ip(self.request), - ) + # print( + # "processing form_valid in IssueCreate for ip address: ", + # get_client_ip(self.request), + # ) reporter_ip = get_client_ip(self.request) form.instance.reporter_ip_address = reporter_ip @@ -971,7 +982,10 @@ def create_issue(self, form): if not domain_exists and (self.request.user.is_authenticated or tokenauth): Points.objects.create( - user=self.request.user, domain=domain, score=1, reason="Domain added" + user=self.request.user, + domain=domain, + score=1, + reason="Domain added", ) messages.success(self.request, "Domain added! + 1") @@ -1119,7 +1133,9 @@ def get_context_data(self, **kwargs): self.request.POST = {} self.request.GET = {} - print("processing get_context_data for ip address: ", get_client_ip(self.request)) + # print( + # "processing get_context_data for ip address: ", get_client_ip(self.request) + # ) context = super(IssueCreate, self).get_context_data(**kwargs) context["activities"] = Issue.objects.exclude( Q(is_hidden=True) & ~Q(user_id=self.request.user.id) @@ -1267,8 +1283,8 @@ class IssueView(DetailView): template_name = "issue.html" def get(self, request, *args, **kwargs): - print("getting issue id: ", self.kwargs["slug"]) - print("getting issue id: ", self.kwargs) + # print("getting issue id: ", self.kwargs["slug"]) + # print("getting issue id: ", self.kwargs) ipdetails = IP() try: id = int(self.kwargs["slug"]) @@ -1283,8 +1299,8 @@ def get(self, request, *args, **kwargs): ipdetails.agent = request.META["HTTP_USER_AGENT"] ipdetails.referer = request.META.get("HTTP_REFERER", None) - print("IP Address: ", ipdetails.address) - print("Issue Number: ", ipdetails.issuenumber) + # print("IP Address: ", ipdetails.address) + # print("Issue Number: ", ipdetails.issuenumber) try: if self.request.user.is_authenticated: @@ -1316,7 +1332,7 @@ def get(self, request, *args, **kwargs): return super(IssueView, self).get(request, *args, **kwargs) def get_context_data(self, **kwargs): - print("getting context data") + # print("getting context data") context = super(IssueView, self).get_context_data(**kwargs) if self.object.user_agent: user_agent = parse(self.object.user_agent) @@ -1333,7 +1349,7 @@ def get_context_data(self, **kwargs): if self.request.user.is_authenticated: context["wallet"] = Wallet.objects.get(user=self.request.user) context["issue_count"] = Issue.objects.filter(url__contains=self.object.domain_name).count() - context["all_comment"] = self.object.comments.all + context["all_comment"] = self.object.comments.all() context["all_users"] = User.objects.all() context["likes"] = UserProfile.objects.filter(issue_upvoted=self.object).count() context["likers"] = UserProfile.objects.filter(issue_upvoted=self.object) @@ -1344,6 +1360,7 @@ def get_context_data(self, **kwargs): context["flagers"] = UserProfile.objects.filter(issue_flaged=self.object) context["screenshots"] = IssueScreenshot.objects.filter(issue=self.object).all() + context["content_type"] = ContentType.objects.get_for_model(Issue).model return context @@ -1440,48 +1457,57 @@ def issue_count(request): @login_required(login_url="/accounts/login") -def delete_comment(request): - int_issue_pk = int(request.POST["issue_pk"]) - issue = get_object_or_404(Issue, pk=int_issue_pk) +def delete_content_comment(request): + content_type = request.POST.get("content_type") + content_pk = int(request.POST.get("content_pk")) + content_type_obj = ContentType.objects.get(model=content_type) + content = content_type_obj.get_object_for_this_type(pk=content_pk) + if request.method == "POST": comment = Comment.objects.get( pk=int(request.POST["comment_pk"]), author=request.user.username ) comment.delete() + context = { - "all_comments": Comment.objects.filter(issue__id=int_issue_pk).order_by("-created_date"), - "object": issue, + "all_comments": Comment.objects.filter( + content_type=content_type_obj, object_id=content_pk + ).order_by("-created_date"), + "object": content, } return render(request, "comments2.html", context) -def update_comment(request, issue_pk, comment_pk): - issue = Issue.objects.filter(pk=issue_pk).first() +def update_content_comment(request, content_pk, comment_pk): + content_type = request.POST.get("content_type") + content_type_obj = ContentType.objects.get(model=content_type) + content = content_type_obj.get_object_for_this_type(pk=content_pk) comment = Comment.objects.filter(pk=comment_pk).first() + if request.method == "POST" and isinstance(request.user, User): comment.text = escape(request.POST.get("comment", "")) comment.save() context = { - "all_comment": Comment.objects.filter(issue__id=issue_pk).order_by("-created_date"), - "object": issue, + "all_comment": Comment.objects.filter( + content_type=content_type_obj, object_id=content_pk + ).order_by("-created_date"), + "object": content, } return render(request, "comments2.html", context) -def comment_on_issue(request, issue_pk): - try: - issue_pk = int(issue_pk) - except ValueError: - raise Http404("Issue does not exist") - issue = Issue.objects.filter(pk=issue_pk).first() +def comment_on_content(request, content_pk): + content_type = request.POST.get("content_type") + content_type_obj = ContentType.objects.get(model=content_type) + content = content_type_obj.get_object_for_this_type(pk=content_pk) if request.method == "POST" and isinstance(request.user, User): comment = escape(request.POST.get("comment", "")) replying_to_input = request.POST.get("replying_to_input", "").split("#") - if issue is None: - Http404("Issue does not exist, cannot comment") + if content is None: + raise Http404("Content does not exist, cannot comment") if len(replying_to_input) == 2: replying_to_user = replying_to_input[0] @@ -1491,11 +1517,12 @@ def comment_on_issue(request, issue_pk): if parent_comment is None: messages.error(request, "Parent comment doesn't exist.") - return redirect(f"/issue/{issue_pk}") + return redirect(f"/{content_type}/{content_pk}") Comment.objects.create( parent=parent_comment, - issue=issue, + content_type=content_type_obj, + object_id=content_pk, author=request.user.username, author_fk=request.user.userprofile, author_url=f"profile/{request.user.username}/", @@ -1504,7 +1531,8 @@ def comment_on_issue(request, issue_pk): else: Comment.objects.create( - issue=issue, + content_type=content_type_obj, + object_id=content_pk, author=request.user.username, author_fk=request.user.userprofile, author_url=f"profile/{request.user.username}/", @@ -1512,8 +1540,10 @@ def comment_on_issue(request, issue_pk): ) context = { - "all_comment": Comment.objects.filter(issue__id=issue_pk).order_by("-created_date"), - "object": issue, + "all_comment": Comment.objects.filter( + content_type=content_type_obj, object_id=content_pk + ).order_by("-created_date"), + "object": content, } return render(request, "comments2.html", context) diff --git a/website/views/project.py b/website/views/project.py index 6703a10db..9b7384686 100644 --- a/website/views/project.py +++ b/website/views/project.py @@ -1,30 +1,38 @@ +import ipaddress import json -import logging import re +import socket +import time from datetime import datetime, timedelta -from io import BytesIO from pathlib import Path +from urllib.parse import urlparse import django_filters -import matplotlib.pyplot as plt + +# import matplotlib.pyplot as plt import requests +from django.conf import settings from django.contrib import messages -from django.contrib.auth.decorators import user_passes_test -from django.db.models import Count, Q -from django.db.models.functions import TruncDate -from django.http import HttpResponse -from django.shortcuts import get_object_or_404, redirect, render -from django.utils.timezone import now +from django.contrib.auth.decorators import login_required, user_passes_test +from django.contrib.humanize.templatetags.humanize import naturaltime +from django.core.exceptions import ValidationError +from django.core.validators import URLValidator +from django.db.models import Q, Sum +from django.http import JsonResponse +from django.shortcuts import redirect, render +from django.utils.dateparse import parse_datetime +from django.utils.text import slugify +from django.utils.timezone import localtime, now +from django.views.decorators.http import require_http_methods from django.views.generic import DetailView, ListView from django_filters.views import FilterView -from rest_framework.views import APIView from website.bitcoin_utils import create_bacon_token from website.forms import GitHubURLForm -from website.models import IP, BaconToken, Contribution, Organization, Project, Repo +from website.models import BaconToken, Contribution, Organization, Project, Repo from website.utils import admin_required -logging.getLogger("matplotlib").setLevel(logging.ERROR) +# logging.getLogger("matplotlib").setLevel(logging.ERROR) def blt_tomato(request): @@ -173,59 +181,59 @@ def get_context_data(self, **kwargs): return context -class ProjectBadgeView(APIView): - def get(self, request, slug): - # Retrieve the project or return 404 - project = get_object_or_404(Project, slug=slug) - - # Get unique visits, grouped by date - visit_counts = ( - IP.objects.filter(path=request.path) - .annotate(date=TruncDate("created")) - .values("date") - .annotate(visit_count=Count("address")) - .order_by("date") # Order from oldest to newest - ) - - # Update project visit count - project.repo_visit_count += 1 - project.save() - - # Extract dates and counts - dates = [entry["date"] for entry in visit_counts] - counts = [entry["visit_count"] for entry in visit_counts] - total_views = sum(counts) # Calculate total views - - fig = plt.figure(figsize=(4, 1)) - plt.bar(dates, counts, width=0.5, color="red") - - plt.title( - f"{total_views}", - loc="left", - x=-0.36, - y=0.3, - fontsize=15, - fontweight="bold", - color="red", - ) - - plt.gca().set_xticks([]) # Remove x-axis ticks - plt.gca().set_yticks([]) - plt.box(False) - - # Save the plot to an in-memory file - buffer = BytesIO() - plt.savefig(buffer, format="png", bbox_inches="tight") - plt.close() - buffer.seek(0) - - # Prepare the HTTP response with the bar graph image - response = HttpResponse(buffer, content_type="image/png") - response["Cache-Control"] = "no-store, no-cache, must-revalidate, max-age=0" - response["Pragma"] = "no-cache" - response["Expires"] = "0" - - return response +# class ProjectBadgeView(APIView): +# def get(self, request, slug): +# # Retrieve the project or return 404 +# project = get_object_or_404(Project, slug=slug) + +# # Get unique visits, grouped by date +# visit_counts = ( +# IP.objects.filter(path=request.path) +# .annotate(date=TruncDate("created")) +# .values("date") +# .annotate(visit_count=Count("address")) +# .order_by("date") # Order from oldest to newest +# ) + +# # Update project visit count +# project.repo_visit_count += 1 +# project.save() + +# # Extract dates and counts +# dates = [entry["date"] for entry in visit_counts] +# counts = [entry["visit_count"] for entry in visit_counts] +# total_views = sum(counts) # Calculate total views + +# fig = plt.figure(figsize=(4, 1)) +# plt.bar(dates, counts, width=0.5, color="red") + +# plt.title( +# f"{total_views}", +# loc="left", +# x=-0.36, +# y=0.3, +# fontsize=15, +# fontweight="bold", +# color="red", +# ) + +# plt.gca().set_xticks([]) # Remove x-axis ticks +# plt.gca().set_yticks([]) +# plt.box(False) + +# # Save the plot to an in-memory file +# buffer = BytesIO() +# plt.savefig(buffer, format="png", bbox_inches="tight") +# plt.close() +# buffer.seek(0) + +# # Prepare the HTTP response with the bar graph image +# response = HttpResponse(buffer, content_type="image/png") +# response["Cache-Control"] = "no-store, no-cache, must-revalidate, max-age=0" +# response["Pragma"] = "no-cache" +# response["Expires"] = "0" + +# return response class ProjectListView(ListView): @@ -398,6 +406,12 @@ def get_queryset(self): def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) + if self.request.user.is_authenticated: + # Get organizations where user is admin or manager + context["user_organizations"] = Organization.objects.filter( + Q(admin=self.request.user) | Q(managers=self.request.user) + ).distinct() + # Get organizations that have projects context["organizations"] = Organization.objects.filter(projects__isnull=False).distinct() @@ -415,3 +429,851 @@ def get_context_data(self, **kwargs): context["projects"] = projects return context + + +@login_required +@require_http_methods(["POST"]) +def create_project(request): + try: + max_retries = 5 + delay = 20 + + def validate_url(url): + """Validate URL format and accessibility""" + if not url: + return True # URLs are optional + + # Validate URL format + try: + URLValidator(schemes=["http", "https"])(url) + parsed = urlparse(url) + hostname = parsed.hostname + if not hostname: + return False + + try: + addr_info = socket.getaddrinfo(hostname, None) + except socket.gaierror: + # DNS resolution failed; hostname is not resolvable + return False + for info in addr_info: + ip = info[4][0] + try: + ip_obj = ipaddress.ip_address(ip) + if ( + ip_obj.is_private + or ip_obj.is_loopback + or ip_obj.is_reserved + or ip_obj.is_multicast + or ip_obj.is_link_local + or ip_obj.is_unspecified + ): + # Disallowed IP range detected + return False + except ValueError: + # Invalid IP address format + return False + return True + + except (ValidationError, ValueError): + return False + + # Validate project URL + project_url = request.POST.get("url") + if project_url and not validate_url(project_url): + return JsonResponse( + { + "error": "Project URL is not accessible or returns an error", + "code": "INVALID_PROJECT_URL", + }, + status=400, + ) + + # Validate social media URLs + twitter = request.POST.get("twitter") + if twitter: + if twitter.startswith(("http://", "https://")): + if not validate_url(twitter): + return JsonResponse( + { + "error": "Twitter URL is not accessible", + "code": "INVALID_TWITTER_URL", + }, + status=400, + ) + elif not twitter.startswith("@"): + twitter = f"@{twitter}" + + facebook = request.POST.get("facebook") + if facebook: + if not validate_url(facebook) or "facebook.com" not in facebook: + return JsonResponse( + { + "error": "Invalid or inaccessible Facebook URL", + "code": "INVALID_FACEBOOK_URL", + }, + status=400, + ) + + # Validate repository URLs + repo_urls = request.POST.getlist("repo_urls[]") + for url in repo_urls: + if not url: + continue + + # Validate GitHub URL format + if not url.startswith("https://github.com/"): + return JsonResponse( + { + "error": f"Repository URL must be a GitHub URL: {url}", + "code": "NON_GITHUB_URL", + }, + status=400, + ) + + # Verify GitHub URL format and accessibility + if not re.match(r"https://github\.com/[^/]+/[^/]+/?$", url): + return JsonResponse( + { + "error": f"Invalid GitHub repository URL format: {url}", + "code": "INVALID_GITHUB_URL_FORMAT", + }, + status=400, + ) + + if not validate_url(url): + return JsonResponse( + { + "error": f"GitHub repository is not accessible: {url}", + "code": "INACCESSIBLE_REPO", + }, + status=400, + ) + + # Check if project already exists by name + project_name = request.POST.get("name") + if Project.objects.filter(name__iexact=project_name).exists(): + return JsonResponse( + { + "error": "A project with this name already exists", + "code": "NAME_EXISTS", + }, + status=409, + ) # 409 Conflict + + # Check if project URL already exists + project_url = request.POST.get("url") + if project_url and Project.objects.filter(url=project_url).exists(): + return JsonResponse( + { + "error": "A project with this URL already exists", + "code": "URL_EXISTS", + }, + status=409, + ) + + # Check if any of the repository URLs are already linked to other projects + repo_urls = request.POST.getlist("repo_urls[]") + existing_repos = Repo.objects.filter(repo_url__in=repo_urls) + if existing_repos.exists(): + existing_urls = list(existing_repos.values_list("repo_url", flat=True)) + return JsonResponse( + { + "error": "One or more repositories are already linked to other projects", + "code": "REPOS_EXIST", + "existing_repos": existing_urls, + }, + status=409, + ) + + # Extract project data + project_data = { + "name": project_name, + "description": request.POST.get("description"), + "url": project_url, + "twitter": request.POST.get("twitter"), + "facebook": request.POST.get("facebook"), + } + + # Handle logo file + if request.FILES.get("logo"): + project_data["logo"] = request.FILES["logo"] + + # Handle organization association + org_id = request.POST.get("organization") + if org_id: + try: + org = Organization.objects.get(id=org_id) + if not (request.user == org.admin): + return JsonResponse( + { + "error": "You do not have permission to add projects to this organization" + }, + status=403, + ) + project_data["organization"] = org + except Organization.DoesNotExist: + return JsonResponse({"error": "Organization not found"}, status=404) + + # Create project + project = Project.objects.create(**project_data) + + # GitHub API configuration + github_token = getattr(settings, "GITHUB_TOKEN", None) + if not github_token: + return JsonResponse({"error": "GitHub token not configured"}, status=500) + + headers = { + "Authorization": f"token {github_token}", + "Accept": "application/vnd.github.v3+json", + } + + # Handle repositories + repo_urls = request.POST.getlist("repo_urls[]") + repo_types = request.POST.getlist("repo_types[]") + + def api_get(url): + for i in range(max_retries): + try: + response = requests.get(url, headers=headers, timeout=10) + except requests.exceptions.RequestException: + time.sleep(delay) + continue + if response.status_code in (403, 429): + time.sleep(delay) + continue + return response + + def get_issue_count(full_name, query): + search_url = f"https://api.github.com/search/issues?q=repo:{full_name}+{query}" + resp = api_get(search_url) + if resp.status_code == 200: + return resp.json().get("total_count", 0) + return 0 + + for url, repo_type in zip(repo_urls, repo_types): + if not url: + continue + + # Convert GitHub URL to API URL + match = re.match(r"https://github.com/([^/]+)/([^/]+)/?", url) + if not match: + continue + + owner, repo_name = match.groups() + api_url = f"https://api.github.com/repos/{owner}/{repo_name}" + + try: + # Fetch repository data + response = requests.get(api_url, headers=headers) + if response.status_code != 200: + continue + + repo_data = response.json() + + # Generate unique slug + base_slug = slugify(repo_data["name"]) + base_slug = base_slug.replace(".", "-") + if len(base_slug) > 50: + base_slug = base_slug[:50] + if not base_slug: + base_slug = f"repo-{int(time.time())}" + + unique_slug = base_slug + counter = 1 + while Repo.objects.filter(slug=unique_slug).exists(): + suffix = f"-{counter}" + if len(base_slug) + len(suffix) > 50: + base_slug = base_slug[: 50 - len(suffix)] + unique_slug = f"{base_slug}{suffix}" + counter += 1 + + # Fetch additional data + # Contributors count and commits count + commit_count = 0 + all_contributors = [] + page = 1 + while True: + contrib_url = f"{api_url}/contributors?anon=true&per_page=100&page={page}" + c_resp = api_get(contrib_url) + if c_resp.status_code != 200: + break + contributors_data = c_resp.json() + if not contributors_data: + break + commit_count += sum(c.get("contributions", 0) for c in contributors_data) + all_contributors.extend(contributors_data) + page += 1 + + # Issues count - Fixed + full_name = repo_data.get("full_name") + open_issues = get_issue_count(full_name, "type:issue+state:open") + closed_issues = get_issue_count(full_name, "type:issue+state:closed") + open_pull_requests = get_issue_count(full_name, "type:pr+state:open") + total_issues = open_issues + closed_issues + + # Latest release + releases_url = f"{api_url}/releases/latest" + release_response = requests.get(releases_url, headers=headers) + release_name = None + release_datetime = None + if release_response.status_code == 200: + release_data = release_response.json() + release_name = release_data.get("name") or release_data.get("tag_name") + release_datetime = release_data.get("published_at") + + # Create repository with fixed issues count + repo = Repo.objects.create( + project=project, + slug=unique_slug, + name=repo_data["name"], + description=repo_data.get("description", ""), + repo_url=url, + homepage_url=repo_data.get("homepage", ""), + is_wiki=(repo_type == "wiki"), + is_main=(repo_type == "main"), + stars=repo_data.get("stargazers_count", 0), + forks=repo_data.get("forks_count", 0), + last_updated=parse_datetime(repo_data.get("updated_at")), + watchers=repo_data.get("watchers_count", 0), + primary_language=repo_data.get("language"), + license=repo_data.get("license", {}).get("name"), + last_commit_date=parse_datetime(repo_data.get("pushed_at")), + network_count=repo_data.get("network_count", 0), + subscribers_count=repo_data.get("subscribers_count", 0), + size=repo_data.get("size", 0), + logo_url=repo_data.get("owner", {}).get("avatar_url", ""), + open_issues=open_issues, + closed_issues=closed_issues, + total_issues=total_issues, + contributor_count=len(all_contributors), + commit_count=commit_count, + release_name=release_name, + release_datetime=( + parse_datetime(release_datetime) if release_datetime else None + ), + open_pull_requests=open_pull_requests, + ) + + except requests.exceptions.RequestException as e: + continue + + return JsonResponse({"message": "Project created successfully"}, status=201) + + except Organization.DoesNotExist: + return JsonResponse( + {"error": "Organization not found", "code": "ORG_NOT_FOUND"}, status=404 + ) + except PermissionError: + return JsonResponse( + { + "error": "You do not have permission to add projects to this organization", + "code": "PERMISSION_DENIED", + }, + status=403, + ) + + +class ProjectsDetailView(DetailView): + model = Project + template_name = "projects/project_detail.html" + context_object_name = "project" + + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + project = self.get_object() + + # Get all repositories associated with the project + repositories = ( + Repo.objects.select_related("project") + .filter(project=project) + .prefetch_related("tags", "contributor") + ) + + # Calculate aggregate metrics + repo_metrics = repositories.aggregate( + total_stars=Sum("stars"), + total_forks=Sum("forks"), + total_issues=Sum("total_issues"), + total_contributors=Sum("contributor_count"), + total_commits=Sum("commit_count"), + total_prs=Sum("open_pull_requests"), + ) + + # Format dates for display + created_date = localtime(project.created) + modified_date = localtime(project.modified) + + # Add computed context + context.update( + { + "repositories": repositories, + "repo_metrics": repo_metrics, + "created_date": { + "full": created_date.strftime("%B %d, %Y"), + "relative": naturaltime(created_date), + }, + "modified_date": { + "full": modified_date.strftime("%B %d, %Y"), + "relative": naturaltime(modified_date), + }, + "show_org_details": self.request.user.is_authenticated + and ( + project.organization + and ( + self.request.user == project.organization.admin + or project.organization.managers.filter(id=self.request.user.id).exists() + ) + ), + } + ) + + # Add organization context if it exists + if project.organization: + context["organization"] = project.organization + + return context + + def get(self, request, *args, **kwargs): + response = super().get(request, *args, **kwargs) + + return response + + +class RepoDetailView(DetailView): + model = Repo + template_name = "projects/repo_detail.html" + context_object_name = "repo" + + def get_github_top_contributors(self, repo_url): + """Fetch top contributors directly from GitHub API""" + try: + # Extract owner/repo from GitHub URL + owner_repo = repo_url.rstrip("/").split("github.com/")[-1] + api_url = f"https://api.github.com/repos/{owner_repo}/contributors?per_page=6" + + headers = { + "Authorization": f"token {settings.GITHUB_TOKEN}", + "Accept": "application/vnd.github.v3+json", + } + + response = requests.get(api_url, headers=headers) + if response.status_code == 200: + return response.json() + return [] + except Exception as e: + print(f"Error fetching GitHub contributors: {e}") + return [] + + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + repo = self.get_object() + + # Get other repos from same project + context["related_repos"] = ( + Repo.objects.filter(project=repo.project) + .exclude(id=repo.id) + .select_related("project")[:5] + ) + + # Get top contributors from GitHub + github_contributors = self.get_github_top_contributors(repo.repo_url) + + if github_contributors: + # Match by github_id instead of username + github_ids = [str(c["id"]) for c in github_contributors] + verified_contributors = repo.contributor.filter( + github_id__in=github_ids + ).select_related() + + # Create a mapping of github_id to database contributor + contributor_map = {str(c.github_id): c for c in verified_contributors} + + # Merge GitHub and database data + merged_contributors = [] + for gh_contrib in github_contributors: + gh_id = str(gh_contrib["id"]) + db_contrib = contributor_map.get(gh_id) + if db_contrib: + merged_contributors.append( + { + "name": db_contrib.name, + "github_id": db_contrib.github_id, + "avatar_url": db_contrib.avatar_url, + "contributions": gh_contrib["contributions"], + "github_url": db_contrib.github_url, + "verified": True, + } + ) + else: + merged_contributors.append( + { + "name": gh_contrib["login"], + "github_id": gh_contrib["id"], + "avatar_url": gh_contrib["avatar_url"], + "contributions": gh_contrib["contributions"], + "github_url": gh_contrib["html_url"], + "verified": False, + } + ) + + context["top_contributors"] = merged_contributors + else: + # Fallback to database contributors if GitHub API fails + context["top_contributors"] = [ + { + "name": c.name, + "github_id": c.github_id, + "avatar_url": c.avatar_url, + "contributions": c.contributions, + "github_url": c.github_url, + "verified": True, + } + for c in repo.contributor.all()[:6] + ] + + return context + + def post(self, request, *args, **kwargs): + def get_issue_count(full_name, query, headers): + search_url = f"https://api.github.com/search/issues?q=repo:{full_name}+{query}" + resp = requests.get(search_url, headers=headers) + if resp.status_code == 200: + return resp.json().get("total_count", 0) + return 0 + + repo = self.get_object() + section = request.POST.get("section") + + if section == "basic": + try: + # Get GitHub API token + github_token = getattr(settings, "GITHUB_TOKEN", None) + if not github_token: + return JsonResponse( + {"status": "error", "message": "GitHub token not configured"}, status=500 + ) + + # Extract owner/repo from GitHub URL + match = re.match(r"https://github.com/([^/]+)/([^/]+)/?", repo.repo_url) + if not match: + return JsonResponse( + {"status": "error", "message": "Invalid repository URL"}, status=400 + ) + + owner, repo_name = match.groups() + api_url = f"https://api.github.com/repos/{owner}/{repo_name}" + + # Make GitHub API request + headers = { + "Authorization": f"token {github_token}", + "Accept": "application/vnd.github.v3+json", + } + response = requests.get(api_url, headers=headers) + + if response.status_code == 200: + data = response.json() + + # Update repo with fresh data + repo.stars = data.get("stargazers_count", 0) + repo.forks = data.get("forks_count", 0) + repo.watchers = data.get("watchers_count", 0) + repo.open_issues = data.get("open_issues_count", 0) + repo.network_count = data.get("network_count", 0) + repo.subscribers_count = data.get("subscribers_count", 0) + repo.last_updated = parse_datetime(data.get("updated_at")) + repo.save() + + return JsonResponse( + { + "status": "success", + "message": "Basic information updated successfully", + "data": { + "stars": repo.stars, + "forks": repo.forks, + "watchers": repo.watchers, + "network_count": repo.network_count, + "subscribers_count": repo.subscribers_count, + "last_updated": naturaltime(repo.last_updated).replace( + "\xa0", " " + ), # Fix unicode space + }, + } + ) + else: + return JsonResponse( + {"status": "error", "message": f"GitHub API error: {response.status_code}"}, + status=response.status_code, + ) + + except requests.RequestException as e: + return JsonResponse( + { + "status": "error", + "message": "Network error: A network error occurred. Please try again later.", + }, + status=503, + ) + except requests.HTTPError as e: + return JsonResponse( + { + "status": "error", + "message": "A GitHub API error occurred. Please try again later.", + }, + status=e.response.status_code, + ) + except ValueError as e: + return JsonResponse( + {"status": "error", "message": "There was an error processing your data."}, + status=400, + ) + + elif section == "metrics": + try: + github_token = getattr(settings, "GITHUB_TOKEN", None) + if not github_token: + return JsonResponse( + {"status": "error", "message": "GitHub token not configured"}, status=500 + ) + + match = re.match(r"https://github.com/([^/]+)/([^/]+)/?", repo.repo_url) + if not match: + return JsonResponse( + {"status": "error", "message": "Invalid repository URL"}, status=400 + ) + + # Extract owner and repo from API call + owner, repo_name = match.groups() + api_url = f"https://api.github.com/repos/{owner}/{repo_name}" + headers = { + "Authorization": f"token {github_token}", + "Accept": "application/vnd.github.v3+json", + } + response = requests.get(api_url, headers=headers) + + if response.status_code != 200: + return JsonResponse( + {"status": "error", "message": "Failed to fetch repository data"}, + status=500, + ) + + repo_data = response.json() + full_name = repo_data.get("full_name") + default_branch = repo_data.get("default_branch") + if not full_name: + return JsonResponse( + {"status": "error", "message": "Could not get repository full name"}, + status=500, + ) + + full_name = full_name.replace(" ", "+") + + # get the total commit + url = f"https://api.github.com/repos/{full_name}/commits" + params = {"per_page": 1, "page": 1} + response = requests.get(url, headers=headers, params=params) + if response.status_code == 200: + if "Link" in response.headers: + links = response.headers["Link"] + last_page = 1 + for link in links.split(","): + if 'rel="last"' in link: + last_page = int(link.split("&page=")[1].split(">")[0]) + commit_count = last_page + else: + commits = response.json() + total_commits = len(commits) + commit_count = total_commits + else: + commit_count = 0 + + # Get open issues and PRs + open_issues = get_issue_count(full_name, "type:issue+state:open", headers) + closed_issues = get_issue_count(full_name, "type:issue+state:closed", headers) + open_pull_requests = get_issue_count(full_name, "type:pr+state:open", headers) + total_issues = open_issues + closed_issues + + if ( + repo.open_issues != open_issues + or repo.closed_issues != closed_issues + or repo.total_issues != total_issues + or repo.open_pull_requests != open_pull_requests + or repo.commit_count != commit_count + ): + # Update repository metrics + repo.open_issues = open_issues + repo.closed_issues = closed_issues + repo.total_issues = total_issues + repo.open_pull_requests = open_pull_requests + repo.commit_count = commit_count + + commits_url = f"{api_url}/commits?sha={default_branch}&per_page=1" + commits_response = requests.get(commits_url, headers=headers) + if commits_response.status_code == 200: + commit_data = commits_response.json() + if commit_data: + date_str = commit_data[0]["commit"]["committer"]["date"] + repo.last_commit_date = parse_datetime(date_str) + repo.save() + + return JsonResponse( + { + "status": "success", + "message": "Activity metrics updated successfully", + "data": { + "open_issues": repo.open_issues, + "closed_issues": repo.closed_issues, + "total_issues": repo.total_issues, + "open_pull_requests": repo.open_pull_requests, + "commit_count": repo.commit_count, + "last_commit_date": repo.last_commit_date.strftime("%b %d, %Y") + if repo.last_commit_date + else "", + }, + } + ) + + except requests.RequestException as e: + return JsonResponse( + { + "status": "error", + "message": "Network error: A network error occurred. Please try again later.", + }, + status=503, + ) + except requests.HTTPError as e: + return JsonResponse( + { + "status": "error", + "message": "A GitHub API error occurred. Please try again later.", + }, + status=e.response.status_code, + ) + except ValueError as e: + return JsonResponse( + {"status": "error", "message": "There was an error processing your data."}, + status=400, + ) + + elif section == "technical": + try: + github_token = getattr(settings, "GITHUB_TOKEN", None) + if not github_token: + return JsonResponse( + {"status": "error", "message": "GitHub token not configured"}, status=500 + ) + + match = re.match(r"https://github.com/([^/]+)/([^/]+)/?", repo.repo_url) + if not match: + return JsonResponse( + {"status": "error", "message": "Invalid repository URL"}, status=400 + ) + + owner, repo_name = match.groups() + api_url = f"https://api.github.com/repos/{owner}/{repo_name}" + headers = { + "Authorization": f"token {github_token}", + "Accept": "application/vnd.github.v3+json", + } + + response = requests.get(api_url, headers=headers) + if response.status_code != 200: + return JsonResponse( + {"status": "error", "message": "Failed to fetch repository data"}, + status=500, + ) + + repo_data = response.json() + + # Update repository technical details + repo.primary_language = repo_data.get("language") + repo.size = repo_data.get("size", 0) + repo.license = repo_data.get("license", {}).get("name") + + # Get latest release info + releases_url = f"{api_url}/releases/latest" + release_response = requests.get(releases_url, headers=headers) + if release_response.status_code == 200: + release_data = release_response.json() + repo.release_name = release_data.get("name") or release_data.get("tag_name") + repo.release_datetime = parse_datetime(release_data.get("published_at")) + + repo.save() + + return JsonResponse( + { + "status": "success", + "message": "Technical information updated successfully", + "data": { + "primary_language": repo.primary_language or "Not specified", + "size": repo.size, + "license": repo.license or "Not specified", + "release_name": repo.release_name or "Not available", + "release_date": repo.release_datetime.strftime("%b %d, %Y") + if repo.release_datetime + else "Not available", + "last_commit_date": repo.last_commit_date.strftime("%b %d, %Y") + if repo.last_commit_date + else "Not available", + }, + } + ) + + except requests.RequestException as e: + return JsonResponse( + { + "status": "error", + "message": "Network error: A network error occurred. Please try again later.", + }, + status=503, + ) + except Exception as e: + return JsonResponse( + {"status": "error", "message": "An unexpected error occurred."}, status=500 + ) + + elif section == "community": + try: + from django.core.management import call_command + + repo = self.get_object() + + # Run sync command + call_command("sync_repo_contributors", "--repo_id", repo.id) + + # Refresh repo instance to get updated contributor_count + repo.refresh_from_db() + + # Fetch real-time top contributors from GitHub + github_contributors = self.get_github_top_contributors(repo.repo_url) + merged_contributors = [] + for gh_contrib in github_contributors: + merged_contributors.append( + { + "name": gh_contrib["login"], + "github_id": gh_contrib["id"], + "avatar_url": gh_contrib["avatar_url"], + "contributions": gh_contrib["contributions"], + "github_url": gh_contrib["html_url"], + "verified": False, + } + ) + + return JsonResponse( + { + "status": "success", + "message": "Fetched real-time contributor data from GitHub.", + "data": { + "contributors": merged_contributors, + "total_contributors": repo.contributor_count, + }, + } + ) + + except ValueError as e: + return JsonResponse( + {"status": "error", "message": "There was an error processing your data."}, + status=400, + ) + + return super().post(request, *args, **kwargs) diff --git a/website/views/user.py b/website/views/user.py index 5883bcc4c..bab63d879 100644 --- a/website/views/user.py +++ b/website/views/user.py @@ -1,5 +1,3 @@ -import hashlib -import hmac import json import os from datetime import datetime, timezone @@ -220,9 +218,19 @@ def get_context_data(self, **kwargs): user = self.object context = super(UserProfileDetailView, self).get_context_data(**kwargs) + milestones = [7, 15, 30, 100, 180, 365] + base_milestone = 0 + next_milestone = 0 + for milestone in milestones: + if user.userprofile.current_streak >= milestone: + base_milestone = milestone + elif user.userprofile.current_streak < milestone: + next_milestone = milestone + break + context["base_milestone"] = base_milestone + context["next_milestone"] = next_milestone # Fetch badges user_badges = UserBadge.objects.filter(user=user).select_related("badge") - context["user_badges"] = user_badges # Add badges to context context["is_mentor"] = UserBadge.objects.filter(user=user, badge__title="Mentor").exists() context["available_badges"] = Badge.objects.all() @@ -904,9 +912,10 @@ def badge_user_list(request, badge_id): def github_webhook(request): if request.method == "POST": # Validate GitHub signature - signature = request.headers.get("X-Hub-Signature-256") - if not validate_signature(request.body, signature): - return JsonResponse({"status": "error", "message": "Unauthorized request"}, status=403) + # this doesn't seem to work? + # signature = request.headers.get("X-Hub-Signature-256") + # if not validate_signature(request.body, signature): + # return JsonResponse({"status": "error", "message": "Unauthorized request"}, status=403) payload = json.loads(request.body) event_type = request.headers.get("X-GitHub-Event", "") @@ -1011,15 +1020,15 @@ def assign_github_badge(user, action_title): print(f"Badge '{action_title}' does not exist.") -def validate_signature(payload, signature): - if not signature: - return False +# def validate_signature(payload, signature): +# if not signature: +# return False - secret = bytes(os.environ.get("GITHUB_ACCESS_TOKEN", ""), "utf-8") - computed_hmac = hmac.new(secret, payload, hashlib.sha256) - computed_signature = f"sha256={computed_hmac.hexdigest()}" +# secret = bytes(os.environ.get("GITHUB_ACCESS_TOKEN", ""), "utf-8") +# computed_hmac = hmac.new(secret, payload, hashlib.sha256) +# computed_signature = f"sha256={computed_hmac.hexdigest()}" - return hmac.compare_digest(computed_signature, signature) +# return hmac.compare_digest(computed_signature, signature) @method_decorator(login_required, name="dispatch")