759 lines
22 KiB
Markdown
759 lines
22 KiB
Markdown
# Модель данных и классов
|
|
|
|
Ниже — диаграммы классов по сервисам (JPA-сущности + репозитории + сервисы),
|
|
ER-модель БД и модель web-слоя.
|
|
|
|
## 1. identity-service
|
|
|
|
```mermaid
|
|
classDiagram
|
|
class Account {
|
|
+UUID id
|
|
+String login
|
|
+String passwordHash
|
|
+LmsRole role
|
|
+AccountStatus status
|
|
+Instant archivedAt
|
|
+Boolean mustChangePassword
|
|
+String tempPassword
|
|
+Instant createdAt
|
|
+Instant updatedAt
|
|
}
|
|
class Student {
|
|
+UUID accountId
|
|
+String studentNo
|
|
+String lastName
|
|
+String firstName
|
|
+String middleName
|
|
+String basis
|
|
+String note
|
|
+StudentStatus status
|
|
+Short enrollmentYear
|
|
+String fullName
|
|
}
|
|
class GroupEntity {
|
|
+UUID id
|
|
+String name
|
|
+String stream
|
|
+Short course
|
|
+Short number
|
|
+String degree
|
|
+Short enrollmentYear
|
|
+GroupStatus status
|
|
+Int taskDurationMin
|
|
+Instant createdAt
|
|
}
|
|
class Enrollment {
|
|
+UUID id
|
|
+Instant enrolledAt
|
|
}
|
|
class MembershipHistory {
|
|
+UUID id
|
|
+UUID studentId
|
|
+UUID fromGroupId
|
|
+UUID toGroupId
|
|
+MembershipAction action
|
|
+String reason
|
|
+UUID actorAccountId
|
|
+UUID promotionRunId
|
|
+Instant at
|
|
}
|
|
class Setting {
|
|
+String key
|
|
+String value
|
|
}
|
|
class LmsRole {
|
|
<<enumeration>>
|
|
ADMIN
|
|
TEACHER
|
|
STUDENT
|
|
}
|
|
|
|
Account "1" --> "1" Student : @MapsId account_id
|
|
GroupEntity "1" --> "*" Enrollment : group_id
|
|
Student "1" --> "*" Enrollment : student_id
|
|
Student "1" --> "*" MembershipHistory : student_id
|
|
GroupEntity "1" --> "*" MembershipHistory : from/to_group_id
|
|
Account --> LmsRole : role
|
|
```
|
|
|
|
```mermaid
|
|
classDiagram
|
|
class AuthController
|
|
class GroupController
|
|
class StudentController
|
|
class AuthService {
|
|
+adminExists() Boolean
|
|
+setup(SetupRequest) AuthResponse
|
|
+login(LoginRequest) AuthResponse
|
|
+me(UUID) MeResponse
|
|
+changePassword(UUID, ChangePasswordRequest)
|
|
}
|
|
class GroupService {
|
|
+list() List~GroupSummary~
|
|
+create(CreateGroupRequest) GroupSummary
|
|
+createStudent(UUID, CreateStudentRequest, UUID) Credential
|
|
+candidates(UUID) List~StudentCandidate~
|
|
+enroll(UUID, UUID, UUID) GroupDetail
|
|
+unenroll(UUID, UUID, UUID) GroupDetail
|
|
+groupOfStudent(UUID) GroupSummary
|
|
+detail(UUID) GroupDetail
|
|
+resetPassword(UUID) ResetPasswordResponse
|
|
+credentials(UUID, String) ExportFile
|
|
}
|
|
class GroupImportService {
|
|
+import(String, ByteArray, UUID) ImportResult
|
|
}
|
|
class GroupCohortResolver {
|
|
+resolve(CreateGroupRequest) GroupCohort
|
|
+validate(GroupCohort) GroupCohort
|
|
+defaultEnrollmentYear(Short) Short
|
|
}
|
|
class AccountRepository
|
|
class GroupRepository
|
|
class EnrollmentRepository
|
|
class StudentRepository
|
|
class MembershipHistoryRepository
|
|
class SettingRepository
|
|
class StudentImportParser {
|
|
+parse(String, ByteArray) ParsedGroup
|
|
}
|
|
class CredentialsExporter {
|
|
+csv(List~StudentRow~) ByteArray
|
|
+xlsx(List~StudentRow~) ByteArray
|
|
}
|
|
class Passwords {
|
|
+generate(Int) String
|
|
}
|
|
|
|
AuthController --> AuthService
|
|
GroupController --> GroupService
|
|
GroupController --> GroupImportService
|
|
StudentController --> GroupService
|
|
AuthService --> AccountRepository
|
|
AuthService --> StudentRepository
|
|
GroupService --> GroupRepository
|
|
GroupService --> EnrollmentRepository
|
|
GroupService --> StudentRepository
|
|
GroupService --> AccountRepository
|
|
GroupService --> MembershipHistoryRepository
|
|
GroupService --> GroupCohortResolver
|
|
GroupImportService --> GroupRepository
|
|
GroupImportService --> EnrollmentRepository
|
|
GroupImportService --> StudentRepository
|
|
GroupImportService --> AccountRepository
|
|
GroupImportService --> MembershipHistoryRepository
|
|
GroupImportService --> StudentImportParser
|
|
GroupService --> CredentialsExporter
|
|
GroupService --> Passwords
|
|
```
|
|
|
|
## 2. academic-service
|
|
|
|
```mermaid
|
|
classDiagram
|
|
class Semester {
|
|
+UUID id
|
|
+String title
|
|
+Int year
|
|
+Boolean active
|
|
}
|
|
class Lab {
|
|
+UUID id
|
|
+Int number
|
|
+String title
|
|
+String descriptionMd
|
|
+Instant deadlineAt
|
|
+Instant defenseAt
|
|
+BigDecimal pointsMax
|
|
+String status
|
|
+Instant createdAt
|
|
}
|
|
class LabLevel {
|
|
+UUID id
|
|
+String code
|
|
+String title
|
|
+String description
|
|
+Int position
|
|
+BigDecimal pointsMax
|
|
}
|
|
class Variant {
|
|
+UUID id
|
|
+String number
|
|
+String title
|
|
+String statementMd
|
|
+Int position
|
|
}
|
|
class ContentChapter {
|
|
+UUID id
|
|
+String title
|
|
+String slug
|
|
+Int position
|
|
+Boolean published
|
|
}
|
|
class ContentSection {
|
|
+UUID id
|
|
+UUID chapterId
|
|
+String title
|
|
+String slug
|
|
+Int position
|
|
+Boolean published
|
|
}
|
|
class ContentArticle {
|
|
+UUID id
|
|
+UUID sectionId
|
|
+String title
|
|
+String slug
|
|
+String bodyMd
|
|
+Int position
|
|
+Boolean published
|
|
}
|
|
|
|
Semester "1" --> "*" Lab : semester_id
|
|
Lab "1" --> "*" LabLevel : lab_id
|
|
LabLevel "1" --> "*" Variant : lab_level_id
|
|
Lab "1" --> "*" Variant : lab_id
|
|
ContentChapter "1" --> "*" ContentSection : chapter_id
|
|
ContentSection "1" --> "*" ContentArticle : section_id
|
|
```
|
|
|
|
```mermaid
|
|
classDiagram
|
|
class LabController
|
|
class ContentPublicController
|
|
class ContentAdminController
|
|
class ContentOrderController
|
|
class LabService {
|
|
+importFromFile(String, ByteArray) LabImportResult
|
|
+list() List~LabSummary~
|
|
+detail(UUID) LabDetail
|
|
+create(LabCreateRequest) LabDetail
|
|
+update(UUID, LabUpdateRequest) LabDetail
|
|
+delete(UUID)
|
|
+toggleStatus(UUID)
|
|
+addLevel(UUID, LevelSaveRequest)
|
|
+renameLevel(UUID, UUID, LevelRenameRequest)
|
|
+deleteLevel(UUID, UUID)
|
|
+addVariant(UUID, VariantSaveRequest)
|
|
+updateVariant(UUID, UUID, VariantSaveRequest)
|
|
+deleteVariant(UUID, UUID)
|
|
}
|
|
class ContentService {
|
|
+tree(Boolean) ContentTree
|
|
+treeAll() ContentTree
|
|
+findArticle(String, String, String, Boolean) ContentArticleView
|
|
+setChapterPublished(UUID, Boolean)
|
|
+setSectionPublished(UUID, Boolean)
|
|
+setArticlePublished(UUID, Boolean)
|
|
+listAll() List~ContentArticleView~
|
|
+createChapter(String, String) ChapterTreeNode
|
|
+createSection(UUID, String, String) SectionTreeNode
|
|
+createArticlePlaced(ArticleCreatePayload) ContentArticleView
|
|
+updateArticle(UUID, String, String)
|
|
+moveArticle(UUID, UUID) ContentArticleView
|
|
+deleteArticle(UUID)
|
|
+importZip(String, ByteArray) ContentImportResult
|
|
+reorder(ReorderTree)
|
|
}
|
|
class LabImportParser {
|
|
+parse(String, ByteArray) LabImportBundle
|
|
}
|
|
class AssetStorage {
|
|
+store(String, ByteArray) StoredAsset
|
|
+read(String) Pair
|
|
}
|
|
class LabRepository
|
|
class LabLevelRepository
|
|
class VariantRepository
|
|
class SemesterRepository
|
|
class ContentChapterRepository
|
|
class ContentSectionRepository
|
|
class ContentArticleRepository
|
|
|
|
note for ContentService "Импорт ZIP — источник истины для глав из архива: устаревшие разделы и статьи удаляются (removed)"
|
|
|
|
LabController --> LabService
|
|
ContentPublicController --> ContentService
|
|
ContentAdminController --> ContentService
|
|
ContentOrderController --> ContentService
|
|
LabService --> LabRepository
|
|
LabService --> LabLevelRepository
|
|
LabService --> VariantRepository
|
|
LabService --> SemesterRepository
|
|
LabService --> LabImportParser
|
|
ContentService --> ContentChapterRepository
|
|
ContentService --> ContentSectionRepository
|
|
ContentService --> ContentArticleRepository
|
|
ContentService --> AssetStorage
|
|
```
|
|
|
|
## 3. progress-service
|
|
|
|
```mermaid
|
|
classDiagram
|
|
class VariantAssignment {
|
|
+UUID id
|
|
+UUID studentId
|
|
+UUID labId
|
|
+UUID variantId
|
|
+Boolean manual
|
|
+Instant assignedAt
|
|
}
|
|
class StudentLabLevel {
|
|
+UUID studentId
|
|
+UUID labId
|
|
+UUID labLevelId
|
|
+Boolean manual
|
|
}
|
|
class LabGrade {
|
|
+UUID studentId
|
|
+UUID labId
|
|
+BigDecimal score
|
|
+String generalComment
|
|
+Instant updatedAt
|
|
}
|
|
class GradePublication {
|
|
+UUID groupId
|
|
+UUID labId
|
|
+Instant publishedAt
|
|
}
|
|
class GradePublicationStudent {
|
|
+UUID groupId
|
|
+UUID labId
|
|
+UUID studentId
|
|
+Instant publishedAt
|
|
}
|
|
class GradeComment {
|
|
+UUID id
|
|
+UUID studentId
|
|
+UUID labId
|
|
+String text
|
|
+String tone
|
|
+Instant createdAt
|
|
}
|
|
class CommentTemplate {
|
|
+UUID id
|
|
+String text
|
|
+String tone
|
|
+Int useCount
|
|
}
|
|
class Submission {
|
|
+UUID id
|
|
+UUID studentId
|
|
+UUID labId
|
|
+Int seq
|
|
+Instant createdAt
|
|
}
|
|
class DefenseSession {
|
|
+UUID id
|
|
+UUID studentId
|
|
+UUID labId
|
|
+String assignmentMd
|
|
+String status
|
|
+String code
|
|
+Instant startedAt
|
|
+Instant readyAt
|
|
+String readyReason
|
|
}
|
|
class DefenseStatus {
|
|
<<enumeration>>
|
|
waiting
|
|
started
|
|
ready
|
|
}
|
|
|
|
GradePublication "1" --> "*" GradePublicationStudent : легаси «вся группа» и персональные
|
|
Note for GradePublicationStudent : персональная публикация оценки/комментариев студенту
|
|
CommentTemplate "1" --> "*" GradeComment : подсказки (text/tone)
|
|
GradeComment "*" --> "1" LabGrade : поясняет оценку
|
|
LabGrade "1" --> "0..1" GradePublication : видна студенту после публикации
|
|
DefenseSession --> DefenseStatus : status
|
|
Note for VariantAssignment : studentId / labId / variantId — идентификаторы других сервисов (FK на уровне БД нет)
|
|
Note for LabGrade : groupId для публикации берётся из identity
|
|
```
|
|
|
|
```mermaid
|
|
classDiagram
|
|
class ProgressController
|
|
class SubmissionController
|
|
class DefenseAdminController
|
|
class DefenseMyController
|
|
class StudentPurgeController
|
|
class GroupLabDataController
|
|
class ProgressService {
|
|
+setLevel(LevelAssignmentRequest, String) LevelAssignmentResponse
|
|
+getLevel(UUID, UUID) StudentLabLevel
|
|
+getLevelsForLab(UUID) List~StudentLabLevel~
|
|
+distribute(DistributeRequest, String) DistributeResult
|
|
+assignWithInfo(AssignRequest, String) VariantAssignmentResponse
|
|
+assignmentsForLabEnriched(UUID, String) List~VariantAssignmentResponse~
|
|
+setGrade(GradeSetRequest) GradeResponse
|
|
+suggestComments(String) List~CommentSuggestion~
|
|
+myComments(UUID) List~GradeComment~
|
|
+getStudentAssignment(UUID, UUID, String) MeAssignmentResponse
|
|
}
|
|
class SubmissionService {
|
|
+submit(UUID, UUID, List~MultipartFile~, String) SubmissionDto
|
|
+mySubmissions(UUID, UUID) List~SubmissionDto~
|
|
+download(UUID, UUID, UUID, Boolean) Pair
|
|
+latestPerStudent(UUID) List~SubmissionDto~
|
|
}
|
|
class DefenseService {
|
|
+syncTimeouts() Int
|
|
+assign(UUID, UUID, String) DefenseDto
|
|
+issueCode(UUID) String
|
|
+reset(UUID)
|
|
+listForLab(UUID) List~DefenseDto~
|
|
+startByCode(UUID, String) DefenseDto
|
|
+finish(UUID, UUID) DefenseDto
|
|
+mySessions(UUID) List~DefenseDto~
|
|
+resetByStudentLab(UUID, UUID)
|
|
}
|
|
class GradePublicationService {
|
|
+state(UUID, UUID) PublicationState
|
|
+setStudents(UUID, UUID, List~UUID~)
|
|
+isPublished(UUID, UUID, UUID) Boolean
|
|
}
|
|
class StudentPurgeService {
|
|
+purge(UUID)
|
|
}
|
|
class AcademicClient {
|
|
+getLab(String, String) LabInfo
|
|
+getLabSummary(String, String) LabInfo
|
|
}
|
|
class IdentityClient {
|
|
+getGroup(String, String) GroupInfo
|
|
+listGroups(String) List~Map~
|
|
}
|
|
class SubmissionStorage {
|
|
+store(UUID, UUID, Int, List~MultipartFile~) StoredSubmission
|
|
+zipOf(Path) ByteArray
|
|
+listFiles(Path) List~String~
|
|
+dirFor(UUID, UUID, Int) Path
|
|
}
|
|
class VariantAssignmentRepository
|
|
class StudentLabLevelRepository
|
|
class LabGradeRepository
|
|
class GradeCommentRepository
|
|
class CommentTemplateRepository
|
|
class SubmissionRepository
|
|
class DefenseSessionRepository
|
|
class GradePublicationRepository
|
|
class GradePublicationStudentRepository
|
|
|
|
ProgressController --> ProgressService
|
|
SubmissionController --> SubmissionService
|
|
DefenseAdminController --> DefenseService
|
|
DefenseMyController --> DefenseService
|
|
StudentPurgeController --> StudentPurgeService
|
|
GroupLabDataController --> ProgressService
|
|
ProgressController --> SubmissionService
|
|
ProgressController --> GradePublicationService
|
|
StudentPurgeService --> SubmissionStorage
|
|
ProgressService --> VariantAssignmentRepository
|
|
ProgressService --> StudentLabLevelRepository
|
|
ProgressService --> LabGradeRepository
|
|
ProgressService --> GradeCommentRepository
|
|
ProgressService --> CommentTemplateRepository
|
|
ProgressService --> AcademicClient
|
|
ProgressService --> IdentityClient
|
|
SubmissionService --> SubmissionRepository
|
|
SubmissionService --> SubmissionStorage
|
|
SubmissionService --> AcademicClient
|
|
DefenseService --> DefenseSessionRepository
|
|
GradePublicationService --> GradePublicationRepository
|
|
GradePublicationService --> GradePublicationStudentRepository
|
|
```
|
|
|
|
## 4.web (BFF)
|
|
|
|
```mermaid
|
|
classDiagram
|
|
class AuthPageController
|
|
class LearnController
|
|
class StudentController
|
|
class AdminGroupController
|
|
class AdminLabController
|
|
class AdminStudentController
|
|
class AdminDistributeController
|
|
class AdminDefenseController
|
|
class AdminUsersController
|
|
class ContentAdminController
|
|
class ContentAssetProxy
|
|
class ApiClient {
|
|
+login(String, String) AuthResponseDto
|
|
+contentTree(String, Boolean) ContentTreeNodeDto
|
|
+contentArticle(String, String, String, String, Boolean) ContentArticleDto
|
|
+setContentPublished(String, String, String, Boolean)
|
|
+suggestComments(String, String) List~CommentSuggestionDto~
|
|
+deleteCommentTemplate(String, String)
|
|
+publicationState(String, String, String) PublicationStateDto
|
|
+setPublication(String, PublicationSetPayload)
|
|
+purgeStudent(String, String)
|
|
+searchStudents(String, String) List~AdminUserDto~
|
|
+deleteStudent(String, String) DeleteUserDto
|
|
+defenseWatch(String, List~String~) List~DefenseDto~
|
|
+myDefenseStatus(String, String) Map
|
|
+groupLabData(String, String) List~Map~
|
|
+groupDetail(String, String) GroupDetailDto
|
|
+distribute(String, DistributeRequestPayload) DistributeResultDto
|
|
+setGrade(String, String, String, String, String) GradeResponseDto
|
|
+submit(String, String, List~MultipartFile~, String) List~SubmissionDto~
|
|
+myGrades(String, String) List~GradeResponseDto~
|
|
}
|
|
class SessionUser {
|
|
+String id
|
|
+String login
|
|
+String role
|
|
+String name
|
|
+Boolean mustChangePassword
|
|
+isAdmin() Boolean
|
|
+isStudent() Boolean
|
|
}
|
|
class SessionKeys {
|
|
+String TOKEN
|
|
+String USER
|
|
}
|
|
class CsrfInterceptor
|
|
class NoCachePageInterceptor
|
|
class AuthInterceptor
|
|
class AdminInterceptor
|
|
class MustChangePasswordInterceptor
|
|
class MarkdownRenderer {
|
|
+render(String) String
|
|
}
|
|
|
|
AuthPageController --> ApiClient
|
|
LearnController --> ApiClient
|
|
StudentController --> ApiClient
|
|
AdminGroupController --> ApiClient
|
|
AdminLabController --> ApiClient
|
|
AdminStudentController --> ApiClient
|
|
AdminDistributeController --> ApiClient
|
|
AdminDefenseController --> ApiClient
|
|
AdminUsersController --> ApiClient
|
|
ContentAdminController --> ApiClient
|
|
ContentAssetProxy --> ApiClient
|
|
LearnController --> MarkdownRenderer
|
|
AuthPageController --> SessionUser
|
|
AuthPageController --> SessionKeys
|
|
CsrfInterceptor ..> SessionKeys
|
|
AuthInterceptor ..> SessionUser
|
|
AdminInterceptor ..> SessionUser
|
|
MustChangePasswordInterceptor ..> SessionUser
|
|
```
|
|
|
|
## 5. ER-модель БД
|
|
|
|
Схемы `identity`, `academic`, `progress` живут в одной БД `lms`.
|
|
Между схемами внешних ключей нет: progress хранит `studentId`/`labId`/`groupId`
|
|
как UUID и получает связанные данные по REST.
|
|
|
|
```mermaid
|
|
erDiagram
|
|
ACCOUNTS {
|
|
uuid id PK
|
|
varchar login UK
|
|
varchar password_hash
|
|
varchar role
|
|
varchar status
|
|
timestamptz archived_at
|
|
boolean must_change_password
|
|
varchar temp_password
|
|
timestamptz created_at
|
|
timestamptz updated_at
|
|
}
|
|
STUDENTS {
|
|
uuid account_id PK
|
|
varchar student_no UK
|
|
varchar last_name
|
|
varchar first_name
|
|
varchar middle_name
|
|
varchar basis
|
|
text note
|
|
varchar status
|
|
smallint enrollment_year
|
|
}
|
|
GROUPS {
|
|
uuid id PK
|
|
varchar name
|
|
varchar stream
|
|
smallint course
|
|
smallint number
|
|
varchar degree
|
|
smallint enrollment_year
|
|
varchar status
|
|
int task_duration_min
|
|
timestamptz created_at
|
|
}
|
|
ENROLLMENTS {
|
|
uuid id PK
|
|
uuid group_id FK
|
|
uuid student_id FK
|
|
timestamptz enrolled_at
|
|
}
|
|
MEMBERSHIP_HISTORY {
|
|
uuid id PK
|
|
uuid student_id FK
|
|
uuid from_group_id FK
|
|
uuid to_group_id FK
|
|
varchar action
|
|
text reason
|
|
uuid actor_account_id
|
|
uuid promotion_run_id
|
|
timestamptz at
|
|
}
|
|
SETTINGS {
|
|
varchar key PK
|
|
varchar value
|
|
}
|
|
|
|
ACCOUNTS ||--o| STUDENTS : "account_id"
|
|
GROUPS ||--o{ ENROLLMENTS : "group_id"
|
|
STUDENTS ||--o{ ENROLLMENTS : "student_id"
|
|
STUDENTS ||--o{ MEMBERSHIP_HISTORY : "student_id"
|
|
GROUPS ||--o{ MEMBERSHIP_HISTORY : "from/to_group_id"
|
|
|
|
SEMESTERS {
|
|
uuid id PK
|
|
varchar title
|
|
int year
|
|
boolean active
|
|
}
|
|
LABS {
|
|
uuid id PK
|
|
int number
|
|
varchar title
|
|
uuid semester_id FK
|
|
text description_md
|
|
timestamptz deadline_at
|
|
timestamptz defense_at
|
|
numeric points_max
|
|
varchar status
|
|
timestamptz created_at
|
|
}
|
|
LAB_LEVELS {
|
|
uuid id PK
|
|
uuid lab_id FK
|
|
varchar code
|
|
varchar title
|
|
text description
|
|
int position
|
|
numeric points_max
|
|
}
|
|
VARIANTS {
|
|
uuid id PK
|
|
uuid lab_id FK
|
|
uuid lab_level_id FK
|
|
varchar number
|
|
varchar title
|
|
text statement_md
|
|
int position
|
|
}
|
|
CHAPTERS {
|
|
uuid id PK
|
|
varchar title
|
|
varchar slug UK
|
|
int position
|
|
boolean published
|
|
}
|
|
SECTIONS {
|
|
uuid id PK
|
|
uuid chapter_id FK
|
|
varchar title
|
|
varchar slug
|
|
int position
|
|
boolean published
|
|
}
|
|
ARTICLES {
|
|
uuid id PK
|
|
uuid section_id FK
|
|
varchar title
|
|
varchar slug
|
|
text body_md
|
|
int position
|
|
boolean published
|
|
}
|
|
|
|
SEMESTERS ||--o{ LABS : "semester_id"
|
|
LABS ||--o{ LAB_LEVELS : "lab_id"
|
|
LAB_LEVELS ||--o{ VARIANTS : "lab_level_id"
|
|
CHAPTERS ||--o{ SECTIONS : "chapter_id"
|
|
SECTIONS ||--o{ ARTICLES : "section_id"
|
|
|
|
VARIANT_ASSIGNMENTS {
|
|
uuid id PK
|
|
uuid student_id
|
|
uuid lab_id
|
|
uuid variant_id
|
|
boolean manual
|
|
timestamptz assigned_at
|
|
}
|
|
STUDENT_LAB_LEVELS {
|
|
uuid student_id PK
|
|
uuid lab_id PK
|
|
uuid lab_level_id
|
|
boolean manual
|
|
}
|
|
LAB_GRADES {
|
|
uuid student_id PK
|
|
uuid lab_id PK
|
|
numeric score
|
|
text general_comment
|
|
timestamptz updated_at
|
|
}
|
|
GRADE_PUBLICATIONS {
|
|
uuid group_id PK
|
|
uuid lab_id PK
|
|
timestamptz published_at
|
|
}
|
|
GRADE_PUBLICATION_STUDENTS {
|
|
uuid group_id PK
|
|
uuid lab_id PK
|
|
uuid student_id PK
|
|
timestamptz published_at
|
|
}
|
|
GRADE_COMMENTS {
|
|
uuid id PK
|
|
uuid student_id
|
|
uuid lab_id
|
|
text text
|
|
varchar tone
|
|
timestamptz created_at
|
|
}
|
|
COMMENT_TEMPLATES {
|
|
uuid id PK
|
|
varchar text UK
|
|
varchar tone
|
|
int use_count
|
|
}
|
|
SUBMISSIONS {
|
|
uuid id PK
|
|
uuid student_id
|
|
uuid lab_id
|
|
int seq
|
|
timestamptz created_at
|
|
}
|
|
DEFENSE_SESSIONS {
|
|
uuid id PK
|
|
uuid student_id
|
|
uuid lab_id
|
|
text assignment_md
|
|
varchar status
|
|
varchar code
|
|
timestamptz started_at
|
|
timestamptz ready_at
|
|
varchar ready_reason
|
|
}
|
|
|
|
VARIANT_ASSIGNMENTS }o--|| VARIANTS : "variant_id (не FK)"
|
|
VARIANT_ASSIGNMENTS }o--|| LABS : "lab_id (не FK)"
|
|
STUDENT_LAB_LEVELS }o--|| LABS : "lab_id (не FK)"
|
|
LAB_GRADES }o--|| LABS : "lab_id (не FK)"
|
|
GRADE_PUBLICATIONS }o--|| LABS : "lab_id (не FK)"
|
|
GRADE_PUBLICATION_STUDENTS }o--|| LABS : "lab_id (не FK)"
|
|
GRADE_COMMENTS }o--|| LABS : "lab_id (не FK)"
|
|
SUBMISSIONS }o--|| LABS : "lab_id (не FK)"
|
|
DEFENSE_SESSIONS }o--|| LABS : "lab_id (не FK)"
|
|
```
|