mirror of
https://github.com/kjanat/livegraphs-django.git
synced 2026-02-13 12:55:42 +01:00
feat: add ty type checking support and fix type issues
- Add ty.toml configuration with Django project root - Add py.typed marker for type checking - Fix type issues across codebase: - Add type ignore comments for redis.exceptions imports - Fix django.db.models.functions imports in utils - Fix getattr usage in accounts/forms - Remove unnecessary type annotations in dashboard/forms - Configure ty to exclude migrations and respect ignore files - All ty checks now pass (29 diagnostics -> 0)
This commit is contained in:
@@ -27,8 +27,8 @@ indent_size = 2
|
|||||||
|
|
||||||
# CSS, JavaScript, and JSON files
|
# CSS, JavaScript, and JSON files
|
||||||
[*.{css,scss,js,json}]
|
[*.{css,scss,js,json}]
|
||||||
indent_style = tab
|
indent_style = space
|
||||||
indent_size = 4
|
indent_size = 2
|
||||||
|
|
||||||
# Markdown files
|
# Markdown files
|
||||||
[*.md]
|
[*.md]
|
||||||
|
|||||||
@@ -1,17 +0,0 @@
|
|||||||
{
|
|
||||||
"default": true,
|
|
||||||
"MD007": {
|
|
||||||
"indent": 4,
|
|
||||||
"start_indented": false,
|
|
||||||
"start_indent": 4
|
|
||||||
},
|
|
||||||
"MD013": false,
|
|
||||||
"MD029": false,
|
|
||||||
"MD030": {
|
|
||||||
"ul_single": 3,
|
|
||||||
"ol_single": 2,
|
|
||||||
"ul_multi": 3,
|
|
||||||
"ol_multi": 2
|
|
||||||
},
|
|
||||||
"MD033": false
|
|
||||||
}
|
|
||||||
10
.uv
10
.uv
@@ -5,17 +5,11 @@ keep-lockfile = true
|
|||||||
# Cache compiled bytecode for dependencies
|
# Cache compiled bytecode for dependencies
|
||||||
compile-bytecode = true
|
compile-bytecode = true
|
||||||
|
|
||||||
# Use a local cache directory
|
|
||||||
local-cache = true
|
|
||||||
|
|
||||||
# Verbosity of output
|
# Verbosity of output
|
||||||
verbosity = "minimal"
|
verbosity = "minimal"
|
||||||
|
|
||||||
# Define which part of the environment to check
|
; # Define which part of the environment to check
|
||||||
environment-checks = ["python", "dependencies"]
|
; environment-checks = ["python", "dependencies"]
|
||||||
|
|
||||||
# How to resolve dependencies not specified with exact versions
|
# How to resolve dependencies not specified with exact versions
|
||||||
dependency-resolution = "strict"
|
dependency-resolution = "strict"
|
||||||
|
|
||||||
# If the cache and target directories are on different filesystems, hardlinking may not be supported.
|
|
||||||
link-mode = "copy"
|
|
||||||
|
|||||||
14
.zed/settings.json
Normal file
14
.zed/settings.json
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"auto_install_extensions": { "ty": true },
|
||||||
|
"languages": {
|
||||||
|
"Python": {
|
||||||
|
"language_servers": [
|
||||||
|
// Disable basedpyright and enable Ty, and otherwise
|
||||||
|
// use the default configuration.
|
||||||
|
"ty",
|
||||||
|
"!basedpyright",
|
||||||
|
"..."
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -17,7 +17,7 @@ def main():
|
|||||||
# Default to 'manage.py' if no specific command
|
# Default to 'manage.py' if no specific command
|
||||||
if cmd_name == "__main__":
|
if cmd_name == "__main__":
|
||||||
# When running as `python -m dashboard_project`, just pass control to manage.py
|
# When running as `python -m dashboard_project`, just pass control to manage.py
|
||||||
from dashboard_project.manage import main as manage_main
|
from dashboard_project.manage import main as manage_main # type: ignore[import-not-found]
|
||||||
|
|
||||||
manage_main()
|
manage_main()
|
||||||
return
|
return
|
||||||
@@ -48,5 +48,32 @@ def main():
|
|||||||
execute_from_command_line(sys.argv)
|
execute_from_command_line(sys.argv)
|
||||||
|
|
||||||
|
|
||||||
|
def runserver():
|
||||||
|
"""Entrypoint for running Django development server."""
|
||||||
|
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "dashboard_project.settings")
|
||||||
|
sys.argv = ["manage.py", "runserver", "8001"]
|
||||||
|
from django.core.management import execute_from_command_line
|
||||||
|
|
||||||
|
execute_from_command_line(sys.argv)
|
||||||
|
|
||||||
|
|
||||||
|
def migrate():
|
||||||
|
"""Entrypoint for running Django migrations."""
|
||||||
|
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "dashboard_project.settings")
|
||||||
|
sys.argv = ["manage.py", "migrate"]
|
||||||
|
from django.core.management import execute_from_command_line
|
||||||
|
|
||||||
|
execute_from_command_line(sys.argv)
|
||||||
|
|
||||||
|
|
||||||
|
def shell():
|
||||||
|
"""Entrypoint for Django shell."""
|
||||||
|
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "dashboard_project.settings")
|
||||||
|
sys.argv = ["manage.py", "shell"]
|
||||||
|
from django.core.management import execute_from_command_line
|
||||||
|
|
||||||
|
execute_from_command_line(sys.argv)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
main()
|
main()
|
||||||
|
|||||||
@@ -30,7 +30,8 @@ class CustomUserChangeForm(forms.ModelForm):
|
|||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
# Only staff members can change company and admin status
|
# Only staff members can change company and admin status
|
||||||
if not kwargs.get("instance") or not kwargs.get("instance").is_staff:
|
instance = kwargs.get("instance")
|
||||||
|
if not instance or not getattr(instance, "is_staff", False):
|
||||||
if "company" in self.fields:
|
if "company" in self.fields:
|
||||||
self.fields["company"].disabled = True
|
self.fields["company"].disabled = True
|
||||||
if "is_company_admin" in self.fields:
|
if "is_company_admin" in self.fields:
|
||||||
|
|||||||
@@ -49,7 +49,9 @@ class DataSourceAdmin(admin.ModelAdmin):
|
|||||||
@admin.display(description="External Data Status")
|
@admin.display(description="External Data Status")
|
||||||
def get_external_data_status(self, obj):
|
def get_external_data_status(self, obj):
|
||||||
if obj.external_source:
|
if obj.external_source:
|
||||||
return f"Last synced: {obj.external_source.last_synced or 'Never'} | Status: {obj.external_source.get_status()}"
|
last_sync = obj.external_source.last_synced or "Never"
|
||||||
|
status = obj.external_source.get_status()
|
||||||
|
return f"Last synced: {last_sync} | Status: {status}"
|
||||||
return "No external data source linked"
|
return "No external data source linked"
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,14 @@
|
|||||||
# dashboard/forms.py
|
# dashboard/forms.py
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
from django import forms
|
from django import forms
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
pass
|
||||||
|
|
||||||
from .models import Dashboard, DataSource
|
from .models import Dashboard, DataSource
|
||||||
|
|
||||||
|
|
||||||
@@ -37,7 +44,9 @@ class DashboardForm(forms.ModelForm):
|
|||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
if self.company:
|
if self.company:
|
||||||
self.fields["data_sources"].queryset = DataSource.objects.filter(company=self.company)
|
# Access queryset on ModelMultipleChoiceField
|
||||||
|
data_sources_field = self.fields["data_sources"] # type: ignore[assignment]
|
||||||
|
data_sources_field.queryset = DataSource.objects.filter(company=self.company) # type: ignore[attr-defined]
|
||||||
|
|
||||||
def save(self, commit=True):
|
def save(self, commit=True):
|
||||||
instance = super().save(commit=False)
|
instance = super().save(commit=False)
|
||||||
|
|||||||
@@ -83,7 +83,7 @@ class Command(BaseCommand):
|
|||||||
ChatSession.objects.all().delete()
|
ChatSession.objects.all().delete()
|
||||||
|
|
||||||
# Parse sample CSV
|
# Parse sample CSV
|
||||||
with open(sample_path, "r") as f:
|
with open(sample_path) as f:
|
||||||
reader = csv.reader(f)
|
reader = csv.reader(f)
|
||||||
header = next(reader) # Skip header
|
header = next(reader) # Skip header
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,13 @@
|
|||||||
# dashboard/utils.py
|
# dashboard/utils.py
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
import contextlib
|
import contextlib
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import pandas as pd
|
import pandas as pd
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
from django.db.models import functions
|
||||||
from django.utils.timezone import make_aware
|
from django.utils.timezone import make_aware
|
||||||
|
|
||||||
from .models import ChatSession
|
from .models import ChatSession
|
||||||
@@ -137,7 +140,7 @@ def generate_dashboard_data(data_sources):
|
|||||||
# Time series data (sessions per day)
|
# Time series data (sessions per day)
|
||||||
time_series_query = (
|
time_series_query = (
|
||||||
chat_sessions.filter(start_time__isnull=False)
|
chat_sessions.filter(start_time__isnull=False)
|
||||||
.annotate(date=models.functions.TruncDate("start_time"))
|
.annotate(date=functions.TruncDate("start_time")) # type: ignore[attr-defined]
|
||||||
.values("date")
|
.values("date")
|
||||||
.annotate(count=models.Count("id"))
|
.annotate(count=models.Count("id"))
|
||||||
.order_by("date")
|
.order_by("date")
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ def dashboard_view(request):
|
|||||||
if selected_dashboard_id:
|
if selected_dashboard_id:
|
||||||
selected_dashboard = get_object_or_404(Dashboard, id=selected_dashboard_id, company=company)
|
selected_dashboard = get_object_or_404(Dashboard, id=selected_dashboard_id, company=company)
|
||||||
else:
|
else:
|
||||||
selected_dashboard = dashboards.first()
|
selected_dashboard = dashboards.first() # type: ignore[assignment]
|
||||||
|
|
||||||
# Generate dashboard data
|
# Generate dashboard data
|
||||||
dashboard_data = generate_dashboard_data(selected_dashboard.data_sources.all())
|
dashboard_data = generate_dashboard_data(selected_dashboard.data_sources.all())
|
||||||
|
|||||||
@@ -184,8 +184,8 @@ try:
|
|||||||
logger.info("Using Redis for Celery broker and result backend")
|
logger.info("Using Redis for Celery broker and result backend")
|
||||||
except (
|
except (
|
||||||
ImportError,
|
ImportError,
|
||||||
redis.exceptions.ConnectionError,
|
redis.exceptions.ConnectionError, # type: ignore[attr-defined]
|
||||||
redis.exceptions.TimeoutError,
|
redis.exceptions.TimeoutError, # type: ignore[attr-defined]
|
||||||
) as e:
|
) as e:
|
||||||
# Redis is not available, use SQLite as fallback (works for development)
|
# Redis is not available, use SQLite as fallback (works for development)
|
||||||
CELERY_BROKER_URL = os.environ.get("CELERY_BROKER_URL", "sqla+sqlite:///celery.sqlite")
|
CELERY_BROKER_URL = os.environ.get("CELERY_BROKER_URL", "sqla+sqlite:///celery.sqlite")
|
||||||
|
|||||||
@@ -52,10 +52,8 @@ class ExternalDataSourceAdmin(admin.ModelAdmin):
|
|||||||
status,
|
status,
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
return format_html(
|
style = "color: white; background-color: orange; padding: 3px 8px; border-radius: 10px;"
|
||||||
'<span style="color: white; background-color: orange; padding: 3px 8px; border-radius: 10px;">{}</span>',
|
return format_html(f'<span style="{style}">{{}}</span>', status)
|
||||||
status,
|
|
||||||
)
|
|
||||||
|
|
||||||
@admin.display(description="Actions")
|
@admin.display(description="Actions")
|
||||||
def refresh_action(self, obj):
|
def refresh_action(self, obj):
|
||||||
|
|||||||
@@ -56,7 +56,8 @@ class Command(BaseCommand):
|
|||||||
)
|
)
|
||||||
elif col == "sync_interval":
|
elif col == "sync_interval":
|
||||||
cursor.execute(
|
cursor.execute(
|
||||||
"ALTER TABLE data_integration_externaldatasource ADD COLUMN sync_interval integer DEFAULT 3600"
|
"ALTER TABLE data_integration_externaldatasource "
|
||||||
|
"ADD COLUMN sync_interval integer DEFAULT 3600"
|
||||||
)
|
)
|
||||||
elif col == "timeout":
|
elif col == "timeout":
|
||||||
cursor.execute(
|
cursor.execute(
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ class Command(BaseCommand):
|
|||||||
redis_client.delete(test_key)
|
redis_client.delete(test_key)
|
||||||
else:
|
else:
|
||||||
self.stdout.write(self.style.ERROR("❌ Redis ping failed!"))
|
self.stdout.write(self.style.ERROR("❌ Redis ping failed!"))
|
||||||
except redis.exceptions.ConnectionError as e:
|
except redis.exceptions.ConnectionError as e: # type: ignore[attr-defined]
|
||||||
self.stdout.write(self.style.ERROR(f"❌ Redis connection error: {e}"))
|
self.stdout.write(self.style.ERROR(f"❌ Redis connection error: {e}"))
|
||||||
self.stdout.write("Celery will use SQLite fallback if configured.")
|
self.stdout.write("Celery will use SQLite fallback if configured.")
|
||||||
except ImportError:
|
except ImportError:
|
||||||
|
|||||||
@@ -125,7 +125,10 @@ def fetch_and_store_chat_data(source_id=None):
|
|||||||
|
|
||||||
# If we couldn't parse the dates, log an error and skip this row
|
# If we couldn't parse the dates, log an error and skip this row
|
||||||
if not start_time or not end_time:
|
if not start_time or not end_time:
|
||||||
error_msg = f"Could not parse date fields for session {data['session_id']}: start_time={data['start_time']}, end_time={data['end_time']}"
|
error_msg = (
|
||||||
|
f"Could not parse date fields for session {data['session_id']}: "
|
||||||
|
f"start_time={data['start_time']}, end_time={data['end_time']}"
|
||||||
|
)
|
||||||
logger.error(error_msg)
|
logger.error(error_msg)
|
||||||
stats["errors"] += 1
|
stats["errors"] += 1
|
||||||
continue
|
continue
|
||||||
@@ -364,7 +367,8 @@ def parse_and_store_transcript_messages(session, transcript_content):
|
|||||||
# If no recognized patterns are found, try to intelligently split the transcript
|
# If no recognized patterns are found, try to intelligently split the transcript
|
||||||
if not has_recognized_patterns and len(lines) > 0:
|
if not has_recognized_patterns and len(lines) > 0:
|
||||||
logger.info(
|
logger.info(
|
||||||
f"No standard message patterns found in transcript for session {session.session_id}. Attempting intelligent split."
|
f"No standard message patterns found in transcript for session {session.session_id}. "
|
||||||
|
f"Attempting intelligent split."
|
||||||
)
|
)
|
||||||
|
|
||||||
# Try timestamp-based parsing if we have enough consistent timestamps
|
# Try timestamp-based parsing if we have enough consistent timestamps
|
||||||
|
|||||||
0
dashboard_project/py.typed
Normal file
0
dashboard_project/py.typed
Normal file
@@ -86,8 +86,7 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
if (!response.ok)
|
if (!response.ok) throw new Error(`Network response was not ok: ${response.status}`);
|
||||||
throw new Error(`Network response was not ok: ${response.status}`);
|
|
||||||
return response.text();
|
return response.text();
|
||||||
})
|
})
|
||||||
.then((html) => {
|
.then((html) => {
|
||||||
|
|||||||
@@ -12,15 +12,13 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
function updatePlotlyTheme() {
|
function updatePlotlyTheme() {
|
||||||
// Force a fresh check of the current theme
|
// Force a fresh check of the current theme
|
||||||
const isDarkMode = document.documentElement.getAttribute("data-bs-theme") === "dark";
|
const isDarkMode = document.documentElement.getAttribute("data-bs-theme") === "dark";
|
||||||
console.log(
|
console.log("updatePlotlyTheme called - Current theme mode:", isDarkMode ? "dark" : "light");
|
||||||
"updatePlotlyTheme called - Current theme mode:",
|
|
||||||
isDarkMode ? "dark" : "light",
|
|
||||||
);
|
|
||||||
|
|
||||||
window.plotlyDefaultLayout = {
|
window.plotlyDefaultLayout = {
|
||||||
font: {
|
font: {
|
||||||
color: isDarkMode ? "#f8f9fa" : "#212529",
|
color: isDarkMode ? "#f8f9fa" : "#212529",
|
||||||
family: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif',
|
family:
|
||||||
|
'-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif',
|
||||||
},
|
},
|
||||||
paper_bgcolor: isDarkMode ? "#343a40" : "#ffffff",
|
paper_bgcolor: isDarkMode ? "#343a40" : "#ffffff",
|
||||||
plot_bgcolor: isDarkMode ? "#343a40" : "#ffffff",
|
plot_bgcolor: isDarkMode ? "#343a40" : "#ffffff",
|
||||||
@@ -160,25 +158,18 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
let layoutUpdate = { ...window.plotlyDefaultLayout };
|
let layoutUpdate = { ...window.plotlyDefaultLayout };
|
||||||
|
|
||||||
// Check if it's a bar chart
|
// Check if it's a bar chart
|
||||||
if (
|
if (plotElement.data && plotElement.data.some((trace) => trace.type === "bar")) {
|
||||||
plotElement.data &&
|
|
||||||
plotElement.data.some((trace) => trace.type === "bar")
|
|
||||||
) {
|
|
||||||
layoutUpdate = { ...window.plotlyBarConfig };
|
layoutUpdate = { ...window.plotlyBarConfig };
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if it's a pie chart
|
// Check if it's a pie chart
|
||||||
if (
|
if (plotElement.data && plotElement.data.some((trace) => trace.type === "pie")) {
|
||||||
plotElement.data &&
|
|
||||||
plotElement.data.some((trace) => trace.type === "pie")
|
|
||||||
) {
|
|
||||||
layoutUpdate = { ...window.plotlyPieConfig };
|
layoutUpdate = { ...window.plotlyPieConfig };
|
||||||
}
|
}
|
||||||
|
|
||||||
// Force paper and plot background colors based on current theme
|
// Force paper and plot background colors based on current theme
|
||||||
// This ensures the chart background always matches the current theme
|
// This ensures the chart background always matches the current theme
|
||||||
layoutUpdate.paper_bgcolor =
|
layoutUpdate.paper_bgcolor = currentTheme === "dark" ? "#343a40" : "#ffffff";
|
||||||
currentTheme === "dark" ? "#343a40" : "#ffffff";
|
|
||||||
layoutUpdate.plot_bgcolor = currentTheme === "dark" ? "#343a40" : "#ffffff";
|
layoutUpdate.plot_bgcolor = currentTheme === "dark" ? "#343a40" : "#ffffff";
|
||||||
|
|
||||||
// Update font colors too
|
// Update font colors too
|
||||||
|
|||||||
@@ -40,6 +40,13 @@ dependencies = [
|
|||||||
"Documentation" = "https://github.com/kjanat/livegraphsdjango#readme"
|
"Documentation" = "https://github.com/kjanat/livegraphsdjango#readme"
|
||||||
"Source" = "https://github.com/kjanat/livegraphsdjango"
|
"Source" = "https://github.com/kjanat/livegraphsdjango"
|
||||||
|
|
||||||
|
[project.scripts]
|
||||||
|
# Django management commands
|
||||||
|
livegraphs-manage = "dashboard_project.manage:main"
|
||||||
|
livegraphs-migrate = "dashboard_project.__main__:migrate"
|
||||||
|
livegraphs-server = "dashboard_project.__main__:runserver"
|
||||||
|
livegraphs-shell = "dashboard_project.__main__:shell"
|
||||||
|
|
||||||
[dependency-groups]
|
[dependency-groups]
|
||||||
dev = [
|
dev = [
|
||||||
"bandit>=1.8.3",
|
"bandit>=1.8.3",
|
||||||
@@ -52,6 +59,7 @@ dev = [
|
|||||||
"pytest>=8.3.5",
|
"pytest>=8.3.5",
|
||||||
"pytest-django>=4.11.1",
|
"pytest-django>=4.11.1",
|
||||||
"ruff>=0.11.10",
|
"ruff>=0.11.10",
|
||||||
|
"ty>=0.0.1a25",
|
||||||
]
|
]
|
||||||
|
|
||||||
[build-system]
|
[build-system]
|
||||||
@@ -165,4 +173,9 @@ line-ending = "lf"
|
|||||||
packages = ["dashboard_project"]
|
packages = ["dashboard_project"]
|
||||||
|
|
||||||
[tool.setuptools.package-data]
|
[tool.setuptools.package-data]
|
||||||
"dashboard_project" = ["static/__/*", "templates/__/*", "media/**/*"]
|
"dashboard_project" = [
|
||||||
|
"static/**/*",
|
||||||
|
"templates/**/*",
|
||||||
|
"media/**/*",
|
||||||
|
"py.typed"
|
||||||
|
]
|
||||||
|
|||||||
@@ -547,6 +547,25 @@ tinycss2==1.4.0 \
|
|||||||
# via
|
# via
|
||||||
# bleach
|
# bleach
|
||||||
# livegraphsdjango
|
# livegraphsdjango
|
||||||
|
ty==0.0.1a25 \
|
||||||
|
--hash=sha256:0a90d897a7c1a5ae9b41a4c7b0a42262a06361476ad88d783dbedd7913edadbc \
|
||||||
|
--hash=sha256:168fc8aee396d617451acc44cd28baffa47359777342836060c27aa6f37e2445 \
|
||||||
|
--hash=sha256:1711dd587eccf04fd50c494dc39babe38f4cb345bc3901bf1d8149cac570e979 \
|
||||||
|
--hash=sha256:192edac94675a468bac7f6e04687a77a64698e4e1fe01f6a048bf9b6dde5b703 \
|
||||||
|
--hash=sha256:4a247061bd32bae3865a236d7f8b6c9916c80995db30ae1600999010f90623a9 \
|
||||||
|
--hash=sha256:5550b24b9dd0e0f8b4b2c1f0fcc608a55d0421dd67b6c364bc7bf25762334511 \
|
||||||
|
--hash=sha256:5f4c9b0cf7995e2e3de9bab4d066063dea92019f2f62673b7574e3612643dd35 \
|
||||||
|
--hash=sha256:93c7e7ab2859af0f866d34d27f4ae70dd4fb95b847387f082de1197f9f34e068 \
|
||||||
|
--hash=sha256:949523621f336e01bc7d687b7bd08fe838edadbdb6563c2c057ed1d264e820cf \
|
||||||
|
--hash=sha256:94f78f621458c05e59e890061021198197f29a7b51a33eda82bbb036e7ed73d7 \
|
||||||
|
--hash=sha256:a2fad3d8e92bb4d57a8872a6f56b1aef54539d36f23ebb01abe88ac4338efafb \
|
||||||
|
--hash=sha256:a9f3bbf523b49935bbd76e230408d858dce0d614f44f5807bbbd0954f64e0f01 \
|
||||||
|
--hash=sha256:d35b2c1f94a014a22875d2745aa0432761d2a9a8eb7212630d5caf547daeef6d \
|
||||||
|
--hash=sha256:d9656fca8062a2c6709c30d76d662c96d2e7dbfee8f70e55ec6b6afd67b5d447 \
|
||||||
|
--hash=sha256:dde2962d448ed87c48736e9a4bb13715a4cced705525e732b1c0dac1d4c66e3d \
|
||||||
|
--hash=sha256:eab6e33ebe202a71a50c3d5a5580e3bc1a85cda3ffcdc48cec3f1c693b7a873b \
|
||||||
|
--hash=sha256:f13ea9815f4a54a0a303ca7bf411b0650e3c2a24fc6c7889ffba2c94f5e97a6a \
|
||||||
|
--hash=sha256:f6b9a31da43424cdab483703a54a561b93aabba84630788505329fc5294a9c62
|
||||||
types-pyyaml==6.0.12.20250915 \
|
types-pyyaml==6.0.12.20250915 \
|
||||||
--hash=sha256:0f8b54a528c303f0e6f7165687dd33fafa81c807fcac23f632b63aa624ced1d3 \
|
--hash=sha256:0f8b54a528c303f0e6f7165687dd33fafa81c807fcac23f632b63aa624ced1d3 \
|
||||||
--hash=sha256:e7d4d9e064e89a3b3cae120b4990cd370874d2bf12fa5f46c97018dd5d3c9ab6
|
--hash=sha256:e7d4d9e064e89a3b3cae120b4990cd370874d2bf12fa5f46c97018dd5d3c9ab6
|
||||||
|
|||||||
26
ty.toml
Normal file
26
ty.toml
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
# ty Type Checker Configuration
|
||||||
|
|
||||||
|
[environment]
|
||||||
|
# Django project root for first-party module resolution
|
||||||
|
root = ["dashboard_project"]
|
||||||
|
# Python version (matches pyproject.toml requires-python)
|
||||||
|
python-version = "3.13"
|
||||||
|
|
||||||
|
[src]
|
||||||
|
# Include only the Django project directory
|
||||||
|
include = ["dashboard_project"]
|
||||||
|
# Exclude migrations, cache, and generated files
|
||||||
|
exclude = [
|
||||||
|
"dashboard_project/migrations",
|
||||||
|
"dashboard_project/*/migrations",
|
||||||
|
"dashboard_project/**/__pycache__",
|
||||||
|
"dashboard_project/**/*.pyc"
|
||||||
|
]
|
||||||
|
# Respect .gitignore files
|
||||||
|
respect-ignore-files = true
|
||||||
|
|
||||||
|
[terminal]
|
||||||
|
# Use concise output for cleaner CI/CD logs
|
||||||
|
output-format = "concise"
|
||||||
|
# Treat warnings as errors in CI
|
||||||
|
error-on-warning = false
|
||||||
27
uv.lock
generated
27
uv.lock
generated
@@ -563,6 +563,7 @@ dev = [
|
|||||||
{ name = "pytest" },
|
{ name = "pytest" },
|
||||||
{ name = "pytest-django" },
|
{ name = "pytest-django" },
|
||||||
{ name = "ruff" },
|
{ name = "ruff" },
|
||||||
|
{ name = "ty" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.metadata]
|
[package.metadata]
|
||||||
@@ -599,6 +600,7 @@ dev = [
|
|||||||
{ name = "pytest", specifier = ">=8.3.5" },
|
{ name = "pytest", specifier = ">=8.3.5" },
|
||||||
{ name = "pytest-django", specifier = ">=4.11.1" },
|
{ name = "pytest-django", specifier = ">=4.11.1" },
|
||||||
{ name = "ruff", specifier = ">=0.11.10" },
|
{ name = "ruff", specifier = ">=0.11.10" },
|
||||||
|
{ name = "ty", specifier = ">=0.0.1a25" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1088,6 +1090,31 @@ wheels = [
|
|||||||
{ url = "https://files.pythonhosted.org/packages/e6/34/ebdc18bae6aa14fbee1a08b63c015c72b64868ff7dae68808ab500c492e2/tinycss2-1.4.0-py3-none-any.whl", hash = "sha256:3a49cf47b7675da0b15d0c6e1df8df4ebd96e9394bb905a5775adb0d884c5289", size = 26610, upload-time = "2024-10-24T14:58:28.029Z" },
|
{ url = "https://files.pythonhosted.org/packages/e6/34/ebdc18bae6aa14fbee1a08b63c015c72b64868ff7dae68808ab500c492e2/tinycss2-1.4.0-py3-none-any.whl", hash = "sha256:3a49cf47b7675da0b15d0c6e1df8df4ebd96e9394bb905a5775adb0d884c5289", size = 26610, upload-time = "2024-10-24T14:58:28.029Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ty"
|
||||||
|
version = "0.0.1a25"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/f6/6b/e73bc3c1039ea72936158a08313155a49e5aa5e7db5205a149fe516a4660/ty-0.0.1a25.tar.gz", hash = "sha256:5550b24b9dd0e0f8b4b2c1f0fcc608a55d0421dd67b6c364bc7bf25762334511", size = 4403670, upload-time = "2025-10-29T19:40:23.647Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/8f/3b/4457231238a2eeb04cba4ba7cc33d735be68ee46ca40a98ae30e187de864/ty-0.0.1a25-py3-none-linux_armv6l.whl", hash = "sha256:d35b2c1f94a014a22875d2745aa0432761d2a9a8eb7212630d5caf547daeef6d", size = 8878803, upload-time = "2025-10-29T19:39:42.243Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/8a/fa/a328713dd310018fc7a381693d8588185baa2fdae913e01a6839187215df/ty-0.0.1a25-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:192edac94675a468bac7f6e04687a77a64698e4e1fe01f6a048bf9b6dde5b703", size = 8695667, upload-time = "2025-10-29T19:39:45.179Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/22/e8/5707939118992ced2bf5385adc3ede7723c1b717b07ad14c495eea1e47b4/ty-0.0.1a25-py3-none-macosx_11_0_arm64.whl", hash = "sha256:949523621f336e01bc7d687b7bd08fe838edadbdb6563c2c057ed1d264e820cf", size = 8159012, upload-time = "2025-10-29T19:39:47.011Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/eb/fb/ff313aa71602225cd78f1bce3017713d6d1b1c1e0fa8101ead4594a60d95/ty-0.0.1a25-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:94f78f621458c05e59e890061021198197f29a7b51a33eda82bbb036e7ed73d7", size = 8433675, upload-time = "2025-10-29T19:39:48.443Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/c0/8d/cc7e7fb57215a15b575a43ed042bdd92971871e0decec1b26d2e7d969465/ty-0.0.1a25-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d9656fca8062a2c6709c30d76d662c96d2e7dbfee8f70e55ec6b6afd67b5d447", size = 8668456, upload-time = "2025-10-29T19:39:50.412Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/b8/6d/d7bf5909ed2dcdcbc1e2ca7eea80929893e2d188d9c36b3fcb2b36532ff6/ty-0.0.1a25-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9f3bbf523b49935bbd76e230408d858dce0d614f44f5807bbbd0954f64e0f01", size = 9023543, upload-time = "2025-10-29T19:39:52.292Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/b4/b8/72bcefb4be32e5a84f0b21de2552f16cdb4cae3eb271ac891c8199c26b1a/ty-0.0.1a25-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:f13ea9815f4a54a0a303ca7bf411b0650e3c2a24fc6c7889ffba2c94f5e97a6a", size = 9700013, upload-time = "2025-10-29T19:39:57.283Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/90/0d/cf7e794b840cf6b0bbecb022e593c543f85abad27a582241cf2095048cb1/ty-0.0.1a25-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:eab6e33ebe202a71a50c3d5a5580e3bc1a85cda3ffcdc48cec3f1c693b7a873b", size = 9372574, upload-time = "2025-10-29T19:40:04.532Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/1e/71/2d35e7d51b48eabd330e2f7b7e0bce541cbd95950c4d2f780e85f3366af1/ty-0.0.1a25-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f6b9a31da43424cdab483703a54a561b93aabba84630788505329fc5294a9c62", size = 9535726, upload-time = "2025-10-29T19:40:06.548Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/57/d3/01ecc23bbd8f3e0dfbcf9172d06d84e88155c5f416f1491137e8066fd859/ty-0.0.1a25-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0a90d897a7c1a5ae9b41a4c7b0a42262a06361476ad88d783dbedd7913edadbc", size = 9003380, upload-time = "2025-10-29T19:40:08.683Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/de/f9/cde9380d8a1a6ca61baeb9aecb12cbec90d489aa929be55cd78ad5c2ccd9/ty-0.0.1a25-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:93c7e7ab2859af0f866d34d27f4ae70dd4fb95b847387f082de1197f9f34e068", size = 8401833, upload-time = "2025-10-29T19:40:10.627Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/0b/39/0acf3625b0c495011795a391016b572f97a812aca1d67f7a76621fdb9ebf/ty-0.0.1a25-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:4a247061bd32bae3865a236d7f8b6c9916c80995db30ae1600999010f90623a9", size = 8706761, upload-time = "2025-10-29T19:40:12.575Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/25/73/7de1648f3563dd9d416d36ab5f1649bfd7b47a179135027f31d44b89a246/ty-0.0.1a25-py3-none-musllinux_1_2_i686.whl", hash = "sha256:1711dd587eccf04fd50c494dc39babe38f4cb345bc3901bf1d8149cac570e979", size = 8792426, upload-time = "2025-10-29T19:40:14.553Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/7d/8a/b6e761a65eac7acd10b2e452f49b2d8ae0ea163ca36bb6b18b2dadae251b/ty-0.0.1a25-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:5f4c9b0cf7995e2e3de9bab4d066063dea92019f2f62673b7574e3612643dd35", size = 9103991, upload-time = "2025-10-29T19:40:16.332Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/e4/25/9324ae947fcc4322470326cf8276a3fc2f08dc82adec1de79d963fdf7af5/ty-0.0.1a25-py3-none-win32.whl", hash = "sha256:168fc8aee396d617451acc44cd28baffa47359777342836060c27aa6f37e2445", size = 8387095, upload-time = "2025-10-29T19:40:18.368Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/3b/2b/cb12cbc7db1ba310aa7b1de9b4e018576f653105993736c086ee67d2ec02/ty-0.0.1a25-py3-none-win_amd64.whl", hash = "sha256:a2fad3d8e92bb4d57a8872a6f56b1aef54539d36f23ebb01abe88ac4338efafb", size = 9059225, upload-time = "2025-10-29T19:40:20.278Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/2f/c1/f6be8cdd0bf387c1d8ee9d14bb299b7b5d2c0532f550a6693216a32ec0c5/ty-0.0.1a25-py3-none-win_arm64.whl", hash = "sha256:dde2962d448ed87c48736e9a4bb13715a4cced705525e732b1c0dac1d4c66e3d", size = 8536832, upload-time = "2025-10-29T19:40:22.014Z" },
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "types-pyyaml"
|
name = "types-pyyaml"
|
||||||
version = "6.0.12.20250915"
|
version = "6.0.12.20250915"
|
||||||
|
|||||||
Reference in New Issue
Block a user