First commit of claude's rework in django + vanillajs fronted
This commit is contained in:
443
models.puml
Normal file
443
models.puml
Normal file
@@ -0,0 +1,443 @@
|
||||
@startuml ShooterHub Data Model
|
||||
|
||||
skinparam classAttributeIconSize 0
|
||||
skinparam classFontSize 12
|
||||
skinparam packageFontSize 13
|
||||
skinparam linetype ortho
|
||||
hide empty members
|
||||
|
||||
' ══════════════════════════════════════════════
|
||||
' PACKAGE: users
|
||||
' ══════════════════════════════════════════════
|
||||
package "users" #EBF5FB {
|
||||
class User {
|
||||
+ id : int
|
||||
+ email : str <<unique>>
|
||||
+ username : str
|
||||
+ display_name : str
|
||||
+ language : str
|
||||
+ avatar : FK → Photo <<nullable>>
|
||||
+ is_active : bool
|
||||
+ is_staff : bool
|
||||
+ date_joined : datetime
|
||||
}
|
||||
}
|
||||
|
||||
' ══════════════════════════════════════════════
|
||||
' PACKAGE: calibers
|
||||
' ══════════════════════════════════════════════
|
||||
package "calibers" #E9F7EF {
|
||||
class Caliber {
|
||||
+ id : int
|
||||
+ name : str
|
||||
+ bullet_diameter_mm : decimal
|
||||
+ case_length_mm : decimal
|
||||
+ status : enum {PENDING, VERIFIED, REJECTED}
|
||||
+ submitted_by : FK → User <<nullable>>
|
||||
+ reviewed_by : FK → User <<nullable>>
|
||||
+ created_at : datetime
|
||||
}
|
||||
}
|
||||
|
||||
' ══════════════════════════════════════════════
|
||||
' PACKAGE: photos
|
||||
' ══════════════════════════════════════════════
|
||||
package "photos" #FEF9E7 {
|
||||
class Photo {
|
||||
+ id : int
|
||||
+ data : bytea
|
||||
+ mime_type : str
|
||||
+ uploaded_at : datetime
|
||||
+ uploaded_by : FK → User <<nullable>>
|
||||
}
|
||||
|
||||
class GroupPhoto {
|
||||
+ id : int
|
||||
+ photo : OneToOne → Photo
|
||||
+ shot_group : FK → ShotGroup <<nullable>>
|
||||
+ caption : str
|
||||
+ is_public : bool
|
||||
+ created_at : datetime
|
||||
}
|
||||
|
||||
class GroupPhotoAnalysis {
|
||||
+ id : int
|
||||
+ group_photo : OneToOne → GroupPhoto
|
||||
+ group_size_mm : decimal <<nullable>>
|
||||
+ group_size_moa : decimal <<nullable>>
|
||||
+ mean_radius_mm : decimal <<nullable>>
|
||||
+ mean_radius_moa : decimal <<nullable>>
|
||||
+ windage_offset_mm : decimal <<nullable>>
|
||||
+ windage_offset_moa : decimal <<nullable>>
|
||||
+ elevation_offset_mm : decimal <<nullable>>
|
||||
+ elevation_offset_moa : decimal <<nullable>>
|
||||
+ computed_at : datetime
|
||||
}
|
||||
|
||||
class PointOfImpact {
|
||||
+ id : int
|
||||
+ group_photo : FK → GroupPhoto
|
||||
+ shot : OneToOne → Shot <<nullable>>
|
||||
+ x_px : float
|
||||
+ y_px : float
|
||||
}
|
||||
}
|
||||
|
||||
' ══════════════════════════════════════════════
|
||||
' PACKAGE: tools
|
||||
' ══════════════════════════════════════════════
|
||||
package "tools" #F4ECF7 {
|
||||
class ChronographAnalysis {
|
||||
+ id : int
|
||||
+ user : FK → User <<nullable>>
|
||||
+ label : str
|
||||
+ distance_m : decimal <<nullable>>
|
||||
+ is_public : bool
|
||||
+ created_at : datetime
|
||||
}
|
||||
|
||||
class ShotGroup {
|
||||
+ id : int
|
||||
+ analysis : FK → ChronographAnalysis
|
||||
+ user : FK → User <<nullable>>
|
||||
+ label : str
|
||||
+ distance_m : decimal <<nullable>>
|
||||
+ ammo : FK → Ammo <<nullable>>
|
||||
+ ammo_batch : FK → ReloadedAmmoBatch <<nullable>>
|
||||
+ firearm : FK → Firearm <<nullable>>
|
||||
}
|
||||
|
||||
class Shot {
|
||||
+ id : int
|
||||
+ group : FK → ShotGroup
|
||||
+ velocity_ms : decimal
|
||||
+ sequence : int
|
||||
+ recorded_at : datetime <<nullable>>
|
||||
}
|
||||
}
|
||||
|
||||
' ══════════════════════════════════════════════
|
||||
' PACKAGE: gears
|
||||
' ══════════════════════════════════════════════
|
||||
package "gears" #FDEDEC {
|
||||
|
||||
abstract class Gear {
|
||||
+ id : int
|
||||
+ brand : str
|
||||
+ model : str
|
||||
+ status : enum {PENDING, VERIFIED, REJECTED}
|
||||
+ submitted_by : FK → User <<nullable>>
|
||||
+ reviewed_by : FK → User <<nullable>>
|
||||
+ created_at : datetime
|
||||
}
|
||||
|
||||
class Firearm {
|
||||
+ gear_ptr : OneToOne → Gear
|
||||
+ caliber : FK → Caliber
|
||||
+ barrel_length_mm : decimal <<nullable>>
|
||||
+ action : str
|
||||
+ firearm_type : str
|
||||
}
|
||||
|
||||
class Scope {
|
||||
+ gear_ptr : OneToOne → Gear
|
||||
+ min_magnification : decimal <<nullable>>
|
||||
+ max_magnification : decimal <<nullable>>
|
||||
+ objective_mm : decimal <<nullable>>
|
||||
+ reticle : str
|
||||
+ click_value_mrad : decimal <<nullable>>
|
||||
}
|
||||
|
||||
class Suppressor {
|
||||
+ gear_ptr : OneToOne → Gear
|
||||
+ caliber : FK → Caliber <<nullable>>
|
||||
+ length_mm : decimal <<nullable>>
|
||||
+ weight_g : decimal <<nullable>>
|
||||
}
|
||||
|
||||
class Bipod {
|
||||
+ gear_ptr : OneToOne → Gear
|
||||
+ min_height_mm : decimal <<nullable>>
|
||||
+ max_height_mm : decimal <<nullable>>
|
||||
}
|
||||
|
||||
class Magazine {
|
||||
+ gear_ptr : OneToOne → Gear
|
||||
+ caliber : FK → Caliber <<nullable>>
|
||||
+ capacity : int <<nullable>>
|
||||
}
|
||||
|
||||
class UserGear {
|
||||
+ id : int
|
||||
+ user : FK → User
|
||||
+ gear : FK → Gear
|
||||
+ serial_number : str
|
||||
+ notes : str
|
||||
+ acquired_at : date <<nullable>>
|
||||
}
|
||||
|
||||
class Rig {
|
||||
+ id : int
|
||||
+ user : FK → User
|
||||
+ label : str
|
||||
+ is_active : bool
|
||||
+ notes : str
|
||||
}
|
||||
|
||||
class RigItem {
|
||||
+ id : int
|
||||
+ rig : FK → Rig
|
||||
+ user_gear : FK → UserGear
|
||||
+ role : str
|
||||
}
|
||||
|
||||
class Ammo {
|
||||
+ id : int
|
||||
+ brand : str
|
||||
+ name : str
|
||||
+ caliber : FK → Caliber <<nullable>>
|
||||
+ bullet_weight_gr : decimal <<nullable>>
|
||||
+ status : enum {PENDING, VERIFIED, REJECTED}
|
||||
+ submitted_by : FK → User <<nullable>>
|
||||
}
|
||||
|
||||
abstract class ComponentMixin {
|
||||
+ id : int
|
||||
+ brand : str
|
||||
+ name : str
|
||||
+ status : enum {PENDING, VERIFIED, REJECTED}
|
||||
+ submitted_by : FK → User <<nullable>>
|
||||
}
|
||||
|
||||
class Primer {
|
||||
+ type : str
|
||||
}
|
||||
|
||||
class Brass {
|
||||
+ caliber : FK → Caliber <<nullable>>
|
||||
}
|
||||
|
||||
class Bullet {
|
||||
+ caliber : FK → Caliber <<nullable>>
|
||||
+ weight_gr : decimal <<nullable>>
|
||||
+ bc_g7 : decimal <<nullable>>
|
||||
+ bc_g1 : decimal <<nullable>>
|
||||
}
|
||||
|
||||
class Powder {
|
||||
+ burn_rate : str
|
||||
}
|
||||
|
||||
class ReloadRecipe {
|
||||
+ id : int
|
||||
+ user : FK → User
|
||||
+ name : str
|
||||
+ caliber : FK → Caliber
|
||||
+ bullet : FK → Bullet
|
||||
+ primer : FK → Primer <<nullable>>
|
||||
+ brass : FK → Brass <<nullable>>
|
||||
+ powder : FK → Powder
|
||||
+ powder_charge_gr : decimal
|
||||
+ coal_mm : decimal <<nullable>>
|
||||
+ notes : str
|
||||
+ created_at : datetime
|
||||
}
|
||||
|
||||
class ReloadedAmmoBatch {
|
||||
+ id : int
|
||||
+ recipe : FK → ReloadRecipe
|
||||
+ user : FK → User
|
||||
+ quantity : int
|
||||
+ lot_number : str
|
||||
+ produced_at : date
|
||||
+ notes : str
|
||||
}
|
||||
}
|
||||
|
||||
' ══════════════════════════════════════════════
|
||||
' PACKAGE: sessions
|
||||
' ══════════════════════════════════════════════
|
||||
package "sessions" #EBF5FB {
|
||||
abstract class AbstractSession {
|
||||
+ id : int
|
||||
+ user : FK → User
|
||||
+ date : date
|
||||
+ label : str
|
||||
+ location : str
|
||||
+ weather : str
|
||||
+ notes : str
|
||||
+ is_public : bool
|
||||
+ firearm : FK → Firearm <<nullable>>
|
||||
+ ammo : FK → Ammo <<nullable>>
|
||||
+ ammo_batch : FK → ReloadedAmmoBatch <<nullable>>
|
||||
+ created_at : datetime
|
||||
}
|
||||
|
||||
class PRSSession {
|
||||
+ total_points : int <<nullable>>
|
||||
+ max_points : int <<nullable>>
|
||||
}
|
||||
|
||||
class PRSStage {
|
||||
+ id : int
|
||||
+ session : FK → PRSSession
|
||||
+ stage_number : int
|
||||
+ points_scored : int <<nullable>>
|
||||
+ max_points : int <<nullable>>
|
||||
+ distance_m : decimal <<nullable>>
|
||||
+ notes : str
|
||||
}
|
||||
|
||||
class FreePracticeSession {
|
||||
+ distance_m : decimal <<nullable>>
|
||||
+ round_count : int <<nullable>>
|
||||
}
|
||||
|
||||
class SpeedShootingSession {
|
||||
+ target_count : int <<nullable>>
|
||||
+ time_seconds : decimal <<nullable>>
|
||||
}
|
||||
}
|
||||
|
||||
' ══════════════════════════════════════════════
|
||||
' PACKAGE: social
|
||||
' ══════════════════════════════════════════════
|
||||
package "social" #FEF9E7 {
|
||||
class Message {
|
||||
+ id : int
|
||||
+ sender : FK → User
|
||||
+ recipient : FK → User
|
||||
+ subject : str
|
||||
+ body : text
|
||||
+ sent_at : datetime
|
||||
+ read_at : datetime <<nullable>>
|
||||
+ deleted_by_sender : bool
|
||||
+ deleted_by_recipient : bool
|
||||
}
|
||||
|
||||
class BlogPost {
|
||||
+ id : int
|
||||
+ author : FK → User
|
||||
+ title : str
|
||||
+ body : text
|
||||
+ is_public : bool
|
||||
+ created_at : datetime
|
||||
+ updated_at : datetime
|
||||
}
|
||||
|
||||
class Bug {
|
||||
+ id : int
|
||||
+ reporter : FK → User <<nullable>>
|
||||
+ title : str
|
||||
+ description : text
|
||||
+ severity : enum {LOW, MEDIUM, HIGH, CRITICAL}
|
||||
+ status : enum {OPEN, IN_PROGRESS, RESOLVED, CLOSED}
|
||||
+ created_at : datetime
|
||||
+ resolved_at : datetime <<nullable>>
|
||||
}
|
||||
|
||||
class Friendship {
|
||||
+ id : int
|
||||
+ from_user : FK → User
|
||||
+ to_user : FK → User
|
||||
+ status : enum {PENDING, ACCEPTED, BLOCKED}
|
||||
+ created_at : datetime
|
||||
+ <<unique>> (from_user, to_user)
|
||||
}
|
||||
}
|
||||
|
||||
' ══════════════════════════════════════════════
|
||||
' RELATIONSHIPS
|
||||
' ══════════════════════════════════════════════
|
||||
|
||||
' users ↔ photos
|
||||
User "0..1" -- "0..1" Photo : avatar >
|
||||
|
||||
' users → calibers
|
||||
User "1" --> "0..*" Caliber : submitted_by >
|
||||
User "0..1" --> "0..*" Caliber : reviewed_by >
|
||||
|
||||
' photos internal
|
||||
Photo "1" <-- "0..1" GroupPhoto : photo
|
||||
GroupPhoto "1" *-- "0..1" GroupPhotoAnalysis : group_photo
|
||||
GroupPhoto "1" *-- "0..*" PointOfImpact : group_photo
|
||||
|
||||
' photos ↔ tools
|
||||
ShotGroup "0..1" <-- "0..*" GroupPhoto : shot_group
|
||||
Shot "0..1" <-- "0..1" PointOfImpact : shot
|
||||
|
||||
' users → photos
|
||||
User "0..1" --> "0..*" Photo : uploaded_by >
|
||||
|
||||
' tools internal
|
||||
ChronographAnalysis "1" *-- "0..*" ShotGroup : analysis
|
||||
ShotGroup "1" *-- "0..*" Shot : group
|
||||
|
||||
' tools ↔ users
|
||||
User "0..1" --> "0..*" ChronographAnalysis : user >
|
||||
User "0..1" --> "0..*" ShotGroup : user >
|
||||
|
||||
' tools ↔ gears
|
||||
Ammo "0..1" <-- "0..*" ShotGroup : ammo
|
||||
ReloadedAmmoBatch "0..1" <-- "0..*" ShotGroup : ammo_batch
|
||||
Firearm "0..1" <-- "0..*" ShotGroup : firearm
|
||||
|
||||
' gears inheritance (MTI)
|
||||
Gear <|-- Firearm
|
||||
Gear <|-- Scope
|
||||
Gear <|-- Suppressor
|
||||
Gear <|-- Bipod
|
||||
Gear <|-- Magazine
|
||||
|
||||
' component inheritance (abstract)
|
||||
ComponentMixin <|-- Primer
|
||||
ComponentMixin <|-- Brass
|
||||
ComponentMixin <|-- Bullet
|
||||
ComponentMixin <|-- Powder
|
||||
|
||||
' gears ↔ calibers
|
||||
Caliber "0..1" <-- "0..*" Firearm : caliber
|
||||
Caliber "0..1" <-- "0..*" Suppressor : caliber
|
||||
Caliber "0..1" <-- "0..*" Magazine : caliber
|
||||
Caliber "1" <-- "0..*" Ammo : caliber
|
||||
Caliber "0..1" <-- "0..*" Brass : caliber
|
||||
Caliber "0..1" <-- "0..*" Bullet : caliber
|
||||
Caliber "1" <-- "0..*" ReloadRecipe : caliber
|
||||
|
||||
' gears ↔ users
|
||||
User "1" --> "0..*" UserGear : user >
|
||||
User "1" --> "0..*" Rig : user >
|
||||
Gear "1" <-- "0..*" UserGear : gear
|
||||
Rig "1" *-- "0..*" RigItem : rig
|
||||
UserGear "1" <-- "0..*" RigItem : user_gear
|
||||
|
||||
' reload chain
|
||||
User "1" --> "0..*" ReloadRecipe : user >
|
||||
User "1" --> "0..*" ReloadedAmmoBatch : user >
|
||||
ReloadRecipe "1" *-- "0..*" ReloadedAmmoBatch : recipe
|
||||
ReloadRecipe "1" --> "1" Bullet : bullet >
|
||||
ReloadRecipe "0..1" --> "0..1" Primer : primer >
|
||||
ReloadRecipe "0..1" --> "0..1" Brass : brass >
|
||||
ReloadRecipe "1" --> "1" Powder : powder >
|
||||
|
||||
' sessions inheritance
|
||||
AbstractSession <|-- PRSSession
|
||||
AbstractSession <|-- FreePracticeSession
|
||||
AbstractSession <|-- SpeedShootingSession
|
||||
PRSSession "1" *-- "0..*" PRSStage : session
|
||||
|
||||
' sessions ↔ users/gears
|
||||
User "1" --> "0..*" AbstractSession : user >
|
||||
Firearm "0..1" <-- "0..*" AbstractSession : firearm
|
||||
Ammo "0..1" <-- "0..*" AbstractSession : ammo
|
||||
ReloadedAmmoBatch "0..1" <-- "0..*" AbstractSession : ammo_batch
|
||||
|
||||
' social ↔ users
|
||||
User "1" --> "0..*" Message : sender >
|
||||
User "1" --> "0..*" Message : recipient >
|
||||
User "1" --> "0..*" BlogPost : author >
|
||||
User "0..1" --> "0..*" Bug : reporter >
|
||||
User "1" --> "0..*" Friendship : from_user >
|
||||
User "1" --> "0..*" Friendship : to_user >
|
||||
|
||||
@enduml
|
||||
Reference in New Issue
Block a user