Coverage for modules\planner\migrations\0001_initial.py: 75%

24 statements  

« prev     ^ index     » next       coverage.py v7.16.0, created at 2026-09-06 17:00 +0800

1# Generated by Django 5.1.15 on 2026-09-02 13:52 

2 

3import django.db.models.deletion 

4import django.utils.timezone 

5import uuid 

6from decimal import Decimal 

7from django.conf import settings 

8from django.db import migrations, models 

9 

10 

11def ensure_planner_schema(apps, schema_editor): 

12 existing_tables = schema_editor.connection.introspection.table_names() 

13 from modules.planner.models import Project, Board, BoardColumn, Sprint, Task, TaskComment, TimeLog 

14 historical_models = { 

15 'Project': Project, 

16 'Board': Board, 

17 'BoardColumn': BoardColumn, 

18 'Sprint': Sprint, 

19 'Task': Task, 

20 'TaskComment': TaskComment, 

21 'TimeLog': TimeLog, 

22 } 

23 

24 if 'planner_project' not in existing_tables: 

25 for name in ('Project', 'Board', 'BoardColumn', 'Sprint', 'Task', 'TaskComment', 'TimeLog'): 

26 schema_editor.create_model(historical_models[name]) 

27 return 

28 

29 with schema_editor.connection.cursor() as cursor: 

30 columns = {row[1] for row in cursor.execute('PRAGMA table_info(planner_project)').fetchall()} 

31 additions = { 

32 'goal': 'TEXT NULL', 

33 'roadmap_start': 'DATE NULL', 

34 'roadmap_end': 'DATE NULL', 

35 'department_id': 'CHAR(32) NULL', 

36 } 

37 for column, definition in additions.items(): 

38 if column not in columns: 

39 schema_editor.execute(f'ALTER TABLE planner_project ADD COLUMN {column} {definition}') 

40 

41 

42class Migration(migrations.Migration): 

43 

44 initial = True 

45 

46 dependencies = [ 

47 ('team', '0002_alter_teammember_role'), 

48 migrations.swappable_dependency(settings.AUTH_USER_MODEL), 

49 ] 

50 

51 operations = [ 

52 migrations.SeparateDatabaseAndState( 

53 state_operations=[ 

54 migrations.CreateModel( 

55 name='Board', 

56 fields=[ 

57 ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), 

58 ('created_at', models.DateTimeField(auto_now_add=True, db_index=True)), 

59 ('updated_at', models.DateTimeField(auto_now=True)), 

60 ('tenant_id', models.UUIDField(db_index=True, help_text='Organization / Tenant ID')), 

61 ('name', models.CharField(default='Main Board', max_length=128)), 

62 ], 

63 options={ 

64 'abstract': False, 

65 }, 

66 ), 

67 migrations.CreateModel( 

68 name='BoardColumn', 

69 fields=[ 

70 ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), 

71 ('created_at', models.DateTimeField(auto_now_add=True, db_index=True)), 

72 ('updated_at', models.DateTimeField(auto_now=True)), 

73 ('tenant_id', models.UUIDField(db_index=True, help_text='Organization / Tenant ID')), 

74 ('name', models.CharField(max_length=64)), 

75 ('order', models.IntegerField(default=0)), 

76 ('limit', models.IntegerField(default=0)), 

77 ('status_category', models.CharField(choices=[('BACKLOG', 'Backlog'), ('TODO', 'To Do'), ('IN_PROGRESS', 'In Progress'), ('IN_REVIEW', 'In Review'), ('DONE', 'Done')], default='TODO', max_length=32)), 

78 ('board', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='columns', to='planner.board')), 

79 ], 

80 options={ 

81 'ordering': ['order'], 

82 }, 

83 ), 

84 migrations.CreateModel( 

85 name='Project', 

86 fields=[ 

87 ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), 

88 ('created_at', models.DateTimeField(auto_now_add=True, db_index=True)), 

89 ('updated_at', models.DateTimeField(auto_now=True)), 

90 ('tenant_id', models.UUIDField(db_index=True, help_text='Organization / Tenant ID')), 

91 ('name', models.CharField(max_length=128)), 

92 ('key', models.CharField(db_index=True, max_length=16)), 

93 ('description', models.TextField(blank=True, null=True)), 

94 ('goal', models.TextField(blank=True, null=True)), 

95 ('roadmap_start', models.DateField(blank=True, null=True)), 

96 ('roadmap_end', models.DateField(blank=True, null=True)), 

97 ('color', models.CharField(default='#6366F1', max_length=32)), 

98 ('status', models.CharField(choices=[('ACTIVE', 'Active'), ('ARCHIVED', 'Archived'), ('COMPLETED', 'Completed')], default='ACTIVE', max_length=16)), 

99 ('department', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='planner_projects', to='team.department')), 

100 ('lead', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='led_projects', to=settings.AUTH_USER_MODEL)), 

101 ], 

102 options={ 

103 'ordering': ['name'], 

104 'unique_together': {('tenant_id', 'key')}, 

105 }, 

106 ), 

107 migrations.AddField( 

108 model_name='board', 

109 name='project', 

110 field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='boards', to='planner.project'), 

111 ), 

112 migrations.CreateModel( 

113 name='Sprint', 

114 fields=[ 

115 ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), 

116 ('created_at', models.DateTimeField(auto_now_add=True, db_index=True)), 

117 ('updated_at', models.DateTimeField(auto_now=True)), 

118 ('tenant_id', models.UUIDField(db_index=True, help_text='Organization / Tenant ID')), 

119 ('name', models.CharField(max_length=128)), 

120 ('goal', models.TextField(blank=True, null=True)), 

121 ('start_date', models.DateField(blank=True, null=True)), 

122 ('end_date', models.DateField(blank=True, null=True)), 

123 ('is_active', models.BooleanField(default=False)), 

124 ('project', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='sprints', to='planner.project')), 

125 ], 

126 options={ 

127 'abstract': False, 

128 }, 

129 ), 

130 migrations.CreateModel( 

131 name='Task', 

132 fields=[ 

133 ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), 

134 ('created_at', models.DateTimeField(auto_now_add=True, db_index=True)), 

135 ('updated_at', models.DateTimeField(auto_now=True)), 

136 ('tenant_id', models.UUIDField(db_index=True, help_text='Organization / Tenant ID')), 

137 ('title', models.CharField(max_length=255)), 

138 ('description', models.TextField(blank=True, null=True)), 

139 ('task_number', models.IntegerField()), 

140 ('priority', models.CharField(choices=[('LOW', 'Low'), ('MEDIUM', 'Medium'), ('HIGH', 'High'), ('URGENT', 'Urgent')], default='MEDIUM', max_length=16)), 

141 ('status', models.CharField(choices=[('BACKLOG', 'Backlog'), ('TODO', 'To Do'), ('IN_PROGRESS', 'In Progress'), ('IN_REVIEW', 'In Review'), ('DONE', 'Done')], default='TODO', max_length=16)), 

142 ('order', models.IntegerField(default=0)), 

143 ('due_date', models.DateField(blank=True, null=True)), 

144 ('estimated_hours', models.DecimalField(decimal_places=2, default=Decimal('0.00'), max_digits=6)), 

145 ('tags', models.JSONField(blank=True, default=list)), 

146 ('assignee', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='assigned_tasks', to=settings.AUTH_USER_MODEL)), 

147 ('board', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='tasks', to='planner.board')), 

148 ('column', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='tasks', to='planner.boardcolumn')), 

149 ('project', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='tasks', to='planner.project')), 

150 ('reporter', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='reported_tasks', to=settings.AUTH_USER_MODEL)), 

151 ('sprint', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='tasks', to='planner.sprint')), 

152 ], 

153 options={ 

154 'ordering': ['order', '-created_at'], 

155 'unique_together': {('project', 'task_number')}, 

156 }, 

157 ), 

158 migrations.CreateModel( 

159 name='TaskComment', 

160 fields=[ 

161 ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), 

162 ('created_at', models.DateTimeField(auto_now_add=True, db_index=True)), 

163 ('updated_at', models.DateTimeField(auto_now=True)), 

164 ('tenant_id', models.UUIDField(db_index=True, help_text='Organization / Tenant ID')), 

165 ('content', models.TextField()), 

166 ('author', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='task_comments', to=settings.AUTH_USER_MODEL)), 

167 ('task', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='comments', to='planner.task')), 

168 ], 

169 options={ 

170 'ordering': ['created_at'], 

171 }, 

172 ), 

173 migrations.CreateModel( 

174 name='TimeLog', 

175 fields=[ 

176 ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), 

177 ('created_at', models.DateTimeField(auto_now_add=True, db_index=True)), 

178 ('updated_at', models.DateTimeField(auto_now=True)), 

179 ('tenant_id', models.UUIDField(db_index=True, help_text='Organization / Tenant ID')), 

180 ('hours', models.DecimalField(decimal_places=2, max_digits=6)), 

181 ('description', models.CharField(blank=True, max_length=255, null=True)), 

182 ('logged_date', models.DateField(default=django.utils.timezone.now)), 

183 ('task', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='time_logs', to='planner.task')), 

184 ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='time_logs', to=settings.AUTH_USER_MODEL)), 

185 ], 

186 options={ 

187 'ordering': ['-logged_date', '-created_at'], 

188 }, 

189 ), 

190 ], 

191 database_operations=[ 

192 migrations.RunPython(ensure_planner_schema, migrations.RunPython.noop), 

193 ], 

194 ), 

195 ]