code-analyser 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- code_analyser-0.1.0.dist-info/METADATA +283 -0
- code_analyser-0.1.0.dist-info/RECORD +34 -0
- code_analyser-0.1.0.dist-info/WHEEL +4 -0
- code_analyser-0.1.0.dist-info/licenses/LICENSE +21 -0
- codelens/__init__.py +7 -0
- codelens/__main__.py +19 -0
- codelens/analyzers/__init__.py +30 -0
- codelens/analyzers/base.py +139 -0
- codelens/analyzers/manager.py +207 -0
- codelens/analyzers/python_analyzer.py +344 -0
- codelens/analyzers/similarity_analyzer.py +512 -0
- codelens/api/__init__.py +1 -0
- codelens/api/routes/__init__.py +1 -0
- codelens/api/routes/analysis.py +441 -0
- codelens/api/routes/reports.py +438 -0
- codelens/api/routes/rubrics.py +349 -0
- codelens/api/schemas.py +305 -0
- codelens/cli.py +297 -0
- codelens/core/__init__.py +1 -0
- codelens/core/config.py +91 -0
- codelens/db/__init__.py +1 -0
- codelens/db/database.py +57 -0
- codelens/main.py +111 -0
- codelens/models/__init__.py +14 -0
- codelens/models/assignments.py +105 -0
- codelens/models/reports.py +172 -0
- codelens/models/rubrics.py +76 -0
- codelens/services/__init__.py +37 -0
- codelens/services/batch_processor.py +508 -0
- codelens/services/code_executor.py +310 -0
- codelens/services/sandbox.py +375 -0
- codelens/services/similarity_service.py +449 -0
- codelens/utils/__init__.py +29 -0
- codelens/utils/helpers.py +217 -0
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Database models for analysis reports and results
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from datetime import datetime
|
|
6
|
+
from typing import TYPE_CHECKING
|
|
7
|
+
|
|
8
|
+
from sqlalchemy import JSON, Boolean, DateTime, Float, ForeignKey, Integer, String, Text
|
|
9
|
+
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
|
10
|
+
from sqlalchemy.sql import func
|
|
11
|
+
|
|
12
|
+
from codelens.db.database import Base
|
|
13
|
+
|
|
14
|
+
if TYPE_CHECKING:
|
|
15
|
+
from .assignments import Assignment
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class AnalysisReport(Base):
|
|
19
|
+
"""Analysis report for a code submission (metadata only, not the code itself)"""
|
|
20
|
+
|
|
21
|
+
__tablename__ = "analysis_reports"
|
|
22
|
+
|
|
23
|
+
id: Mapped[int] = mapped_column(Integer, primary_key=True, index=True)
|
|
24
|
+
|
|
25
|
+
# Assignment and student information
|
|
26
|
+
assignment_id: Mapped[int] = mapped_column(
|
|
27
|
+
Integer, ForeignKey("assignments.id"), nullable=False, index=True
|
|
28
|
+
)
|
|
29
|
+
student_id: Mapped[str | None] = mapped_column(String(100), nullable=True, index=True)
|
|
30
|
+
student_name: Mapped[str | None] = mapped_column(String(200), nullable=True)
|
|
31
|
+
submission_id: Mapped[str] = mapped_column(String(100), nullable=False, index=True) # Unique submission ID
|
|
32
|
+
|
|
33
|
+
# File information (metadata only)
|
|
34
|
+
file_name: Mapped[str] = mapped_column(String(255), nullable=False)
|
|
35
|
+
file_size: Mapped[int] = mapped_column(Integer, nullable=False) # in bytes
|
|
36
|
+
file_hash: Mapped[str] = mapped_column(String(64), nullable=False, index=True) # SHA-256 hash
|
|
37
|
+
language: Mapped[str] = mapped_column(String(50), nullable=False)
|
|
38
|
+
|
|
39
|
+
# Analysis results
|
|
40
|
+
analysis_version: Mapped[str] = mapped_column(String(20), nullable=False) # Version of analysis tools
|
|
41
|
+
|
|
42
|
+
# Syntax and validation results
|
|
43
|
+
syntax_valid: Mapped[bool] = mapped_column(Boolean, nullable=False)
|
|
44
|
+
syntax_errors: Mapped[dict | None] = mapped_column(JSON, nullable=True)
|
|
45
|
+
|
|
46
|
+
# Code quality metrics
|
|
47
|
+
quality_metrics: Mapped[dict] = mapped_column(JSON, nullable=False)
|
|
48
|
+
# Expected structure: {
|
|
49
|
+
# "complexity": {"cyclomatic": 5, "cognitive": 8},
|
|
50
|
+
# "lines_of_code": 120,
|
|
51
|
+
# "style_issues": [...],
|
|
52
|
+
# "type_issues": [...],
|
|
53
|
+
# "documentation_score": 0.75
|
|
54
|
+
# }
|
|
55
|
+
|
|
56
|
+
# Test execution results
|
|
57
|
+
test_results: Mapped[dict | None] = mapped_column(JSON, nullable=True)
|
|
58
|
+
# Expected structure: {
|
|
59
|
+
# "total_tests": 10,
|
|
60
|
+
# "passed_tests": 8,
|
|
61
|
+
# "failed_tests": [...],
|
|
62
|
+
# "execution_time": 1.23,
|
|
63
|
+
# "memory_usage": "45MB"
|
|
64
|
+
# }
|
|
65
|
+
|
|
66
|
+
# Grading results
|
|
67
|
+
grade_breakdown: Mapped[dict] = mapped_column(JSON, nullable=False)
|
|
68
|
+
# Expected structure: {
|
|
69
|
+
# "functionality": 85,
|
|
70
|
+
# "style": 90,
|
|
71
|
+
# "documentation": 70,
|
|
72
|
+
# "testing": 95,
|
|
73
|
+
# "total": 85
|
|
74
|
+
# }
|
|
75
|
+
total_score: Mapped[float] = mapped_column(Float, nullable=False)
|
|
76
|
+
max_score: Mapped[float] = mapped_column(Float, nullable=False, default=100.0)
|
|
77
|
+
|
|
78
|
+
# Similarity analysis
|
|
79
|
+
similarity_results: Mapped[dict | None] = mapped_column(JSON, nullable=True)
|
|
80
|
+
# Expected structure: {
|
|
81
|
+
# "highest_similarity": 0.15,
|
|
82
|
+
# "flagged_submissions": [...],
|
|
83
|
+
# "ai_baseline_similarity": 0.45,
|
|
84
|
+
# "methods_used": ["ast_similarity", "token_similarity"]
|
|
85
|
+
# }
|
|
86
|
+
|
|
87
|
+
# Feedback and recommendations
|
|
88
|
+
feedback: Mapped[dict] = mapped_column(JSON, nullable=False)
|
|
89
|
+
# Expected structure: {
|
|
90
|
+
# "strengths": [...],
|
|
91
|
+
# "improvements": [...],
|
|
92
|
+
# "resources": [...],
|
|
93
|
+
# "detailed_comments": {...}
|
|
94
|
+
# }
|
|
95
|
+
|
|
96
|
+
# Processing metadata
|
|
97
|
+
processing_time: Mapped[float] = mapped_column(Float, nullable=False) # seconds
|
|
98
|
+
tools_used: Mapped[dict] = mapped_column(JSON, nullable=False) # Which analysis tools were used
|
|
99
|
+
|
|
100
|
+
# Status and timestamps
|
|
101
|
+
status: Mapped[str] = mapped_column(String(20), nullable=False, default="completed") # pending, processing, completed, failed
|
|
102
|
+
error_message: Mapped[str | None] = mapped_column(Text, nullable=True)
|
|
103
|
+
|
|
104
|
+
analyzed_at: Mapped[datetime] = mapped_column(
|
|
105
|
+
DateTime(timezone=True),
|
|
106
|
+
server_default=func.now(),
|
|
107
|
+
nullable=False
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
# Relationships
|
|
111
|
+
assignment: Mapped["Assignment"] = relationship("Assignment", back_populates="reports")
|
|
112
|
+
similarity_matches: Mapped[list["SimilarityMatch"]] = relationship(
|
|
113
|
+
"SimilarityMatch",
|
|
114
|
+
foreign_keys="SimilarityMatch.report_id",
|
|
115
|
+
back_populates="report",
|
|
116
|
+
cascade="all, delete-orphan"
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
class SimilarityMatch(Base):
|
|
121
|
+
"""Records of similarity matches between submissions"""
|
|
122
|
+
|
|
123
|
+
__tablename__ = "similarity_matches"
|
|
124
|
+
|
|
125
|
+
id: Mapped[int] = mapped_column(Integer, primary_key=True, index=True)
|
|
126
|
+
|
|
127
|
+
# Source and target reports
|
|
128
|
+
report_id: Mapped[int] = mapped_column(
|
|
129
|
+
Integer, ForeignKey("analysis_reports.id"), nullable=False, index=True
|
|
130
|
+
)
|
|
131
|
+
matched_report_id: Mapped[int] = mapped_column(
|
|
132
|
+
Integer, ForeignKey("analysis_reports.id"), nullable=False, index=True
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
# Similarity metrics
|
|
136
|
+
similarity_score: Mapped[float] = mapped_column(Float, nullable=False)
|
|
137
|
+
similarity_method: Mapped[str] = mapped_column(String(50), nullable=False) # ast, token, etc
|
|
138
|
+
|
|
139
|
+
# Match details
|
|
140
|
+
matched_sections: Mapped[dict] = mapped_column(JSON, nullable=False)
|
|
141
|
+
# Expected structure: {
|
|
142
|
+
# "functions": [...],
|
|
143
|
+
# "code_blocks": [...],
|
|
144
|
+
# "variable_patterns": [...]
|
|
145
|
+
# }
|
|
146
|
+
|
|
147
|
+
confidence: Mapped[float] = mapped_column(Float, nullable=False) # 0.0 - 1.0
|
|
148
|
+
flagged: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)
|
|
149
|
+
|
|
150
|
+
# Review status
|
|
151
|
+
reviewed: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)
|
|
152
|
+
review_decision: Mapped[str | None] = mapped_column(String(20), nullable=True) # flagged, cleared, escalated
|
|
153
|
+
reviewer_notes: Mapped[str | None] = mapped_column(Text, nullable=True)
|
|
154
|
+
|
|
155
|
+
# Timestamps
|
|
156
|
+
detected_at: Mapped[datetime] = mapped_column(
|
|
157
|
+
DateTime(timezone=True),
|
|
158
|
+
server_default=func.now(),
|
|
159
|
+
nullable=False
|
|
160
|
+
)
|
|
161
|
+
reviewed_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
|
|
162
|
+
|
|
163
|
+
# Relationships
|
|
164
|
+
report: Mapped["AnalysisReport"] = relationship(
|
|
165
|
+
"AnalysisReport",
|
|
166
|
+
foreign_keys=[report_id],
|
|
167
|
+
back_populates="similarity_matches"
|
|
168
|
+
)
|
|
169
|
+
matched_report: Mapped["AnalysisReport"] = relationship(
|
|
170
|
+
"AnalysisReport",
|
|
171
|
+
foreign_keys=[matched_report_id]
|
|
172
|
+
)
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Database models for rubrics and grading criteria
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from datetime import datetime
|
|
6
|
+
from typing import TYPE_CHECKING
|
|
7
|
+
|
|
8
|
+
from sqlalchemy import JSON, DateTime, Float, Integer, String, Text
|
|
9
|
+
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
|
10
|
+
from sqlalchemy.sql import func
|
|
11
|
+
|
|
12
|
+
from codelens.db.database import Base
|
|
13
|
+
|
|
14
|
+
if TYPE_CHECKING:
|
|
15
|
+
from .assignments import Assignment
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class Rubric(Base):
|
|
19
|
+
"""Rubric for grading assignments"""
|
|
20
|
+
|
|
21
|
+
__tablename__ = "rubrics"
|
|
22
|
+
|
|
23
|
+
id: Mapped[int] = mapped_column(Integer, primary_key=True, index=True)
|
|
24
|
+
name: Mapped[str] = mapped_column(String(200), nullable=False, index=True)
|
|
25
|
+
description: Mapped[str | None] = mapped_column(Text, nullable=True)
|
|
26
|
+
language: Mapped[str] = mapped_column(String(50), nullable=False, index=True)
|
|
27
|
+
|
|
28
|
+
# Rubric configuration
|
|
29
|
+
criteria: Mapped[dict] = mapped_column(JSON, nullable=False) # Grading criteria
|
|
30
|
+
weights: Mapped[dict] = mapped_column(JSON, nullable=False) # Category weights
|
|
31
|
+
total_points: Mapped[int] = mapped_column(Integer, nullable=False, default=100)
|
|
32
|
+
|
|
33
|
+
# Analysis configuration
|
|
34
|
+
analysis_config: Mapped[dict] = mapped_column(JSON, nullable=True) # Tool configs
|
|
35
|
+
|
|
36
|
+
# Metadata
|
|
37
|
+
created_at: Mapped[datetime] = mapped_column(
|
|
38
|
+
DateTime(timezone=True),
|
|
39
|
+
server_default=func.now(),
|
|
40
|
+
nullable=False
|
|
41
|
+
)
|
|
42
|
+
updated_at: Mapped[datetime] = mapped_column(
|
|
43
|
+
DateTime(timezone=True),
|
|
44
|
+
server_default=func.now(),
|
|
45
|
+
onupdate=func.now(),
|
|
46
|
+
nullable=False
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
# Relationships
|
|
50
|
+
assignments: Mapped[list["Assignment"]] = relationship(
|
|
51
|
+
"Assignment", back_populates="rubric", cascade="all, delete-orphan"
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class RubricCriterion(Base):
|
|
56
|
+
"""Individual criterion within a rubric"""
|
|
57
|
+
|
|
58
|
+
__tablename__ = "rubric_criteria"
|
|
59
|
+
|
|
60
|
+
id: Mapped[int] = mapped_column(Integer, primary_key=True, index=True)
|
|
61
|
+
rubric_id: Mapped[int] = mapped_column(Integer, nullable=False, index=True)
|
|
62
|
+
|
|
63
|
+
# Criterion details
|
|
64
|
+
name: Mapped[str] = mapped_column(String(100), nullable=False)
|
|
65
|
+
description: Mapped[str] = mapped_column(Text, nullable=False)
|
|
66
|
+
category: Mapped[str] = mapped_column(String(50), nullable=False) # functionality, style, etc
|
|
67
|
+
max_points: Mapped[int] = mapped_column(Integer, nullable=False)
|
|
68
|
+
weight: Mapped[float] = mapped_column(Float, nullable=False, default=1.0)
|
|
69
|
+
|
|
70
|
+
# Auto-grading configuration
|
|
71
|
+
auto_gradable: Mapped[bool] = mapped_column(nullable=False, default=True)
|
|
72
|
+
evaluation_method: Mapped[str | None] = mapped_column(String(50), nullable=True) # test_count, complexity, etc
|
|
73
|
+
evaluation_config: Mapped[dict | None] = mapped_column(JSON, nullable=True)
|
|
74
|
+
|
|
75
|
+
# Performance levels (excellent, good, satisfactory, needs_improvement)
|
|
76
|
+
performance_levels: Mapped[dict] = mapped_column(JSON, nullable=False)
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"""Business logic services"""
|
|
2
|
+
|
|
3
|
+
from .batch_processor import (
|
|
4
|
+
BatchFile,
|
|
5
|
+
BatchProcessingConfig,
|
|
6
|
+
BatchProcessingResult,
|
|
7
|
+
BatchProcessor,
|
|
8
|
+
batch_processor,
|
|
9
|
+
)
|
|
10
|
+
from .code_executor import (
|
|
11
|
+
CodeExecutionRequest,
|
|
12
|
+
CodeExecutionResponse,
|
|
13
|
+
CodeExecutorService,
|
|
14
|
+
ValidationResult,
|
|
15
|
+
code_executor,
|
|
16
|
+
)
|
|
17
|
+
from .sandbox import DockerSandbox, ExecutionResult, TestResult, sandbox
|
|
18
|
+
from .similarity_service import SimilarityService, similarity_service
|
|
19
|
+
|
|
20
|
+
__all__ = [
|
|
21
|
+
"DockerSandbox",
|
|
22
|
+
"ExecutionResult",
|
|
23
|
+
"TestResult",
|
|
24
|
+
"sandbox",
|
|
25
|
+
"CodeExecutorService",
|
|
26
|
+
"CodeExecutionRequest",
|
|
27
|
+
"CodeExecutionResponse",
|
|
28
|
+
"ValidationResult",
|
|
29
|
+
"code_executor",
|
|
30
|
+
"BatchProcessor",
|
|
31
|
+
"BatchProcessingConfig",
|
|
32
|
+
"BatchFile",
|
|
33
|
+
"BatchProcessingResult",
|
|
34
|
+
"batch_processor",
|
|
35
|
+
"SimilarityService",
|
|
36
|
+
"similarity_service",
|
|
37
|
+
]
|