Commit 9d12be5886380dfcbdc0e2859eac1cfae04fdd78
1 parent
03c5ff87
Exists in
dev
transfer common admins & models into dashboard
Showing
3 changed files
with
627 additions
and
2 deletions
Show diff stats
CHANGELOG
src/core/pyros_django/dashboard/admin.py
1 | + | |
2 | +# Django imports | |
3 | +from django import forms | |
1 | 4 | from django.contrib import admin |
5 | +from django.contrib.auth.models import User | |
6 | +# EP | |
7 | +from django.conf import settings | |
8 | + | |
9 | +# Project imports | |
10 | +from user_mgmt.models import Country, Institute, PyrosUser, UserLevel, ScientificProgram | |
11 | +from common.models import * | |
12 | +from majordome.models import * | |
13 | +from env_monitor.models import * | |
14 | +from devices.models import Detector, Filter, AgentDeviceStatus, FilterWheel, Telescope, PlcDevice, PlcDeviceStatus | |
15 | +from seq_submit.models import Image, Schedule, Sequence, Album, Plan, ScheduleHasSequences #, StrategyObs, NrtAnalysis | |
16 | +#from seq_submit.models import Image, StrategyObs, Schedule, Request, Alert, Sequence, Album, Plan, NrtAnalysis, ScheduleHasSequences | |
17 | + | |
18 | + | |
19 | +# EP added | |
20 | +class ReadOnlyModelAdmin(admin.ModelAdmin): | |
21 | + """ModelAdmin class that prevents modifications through the admin. | |
22 | + The changelist and the detail view work, but a 403 is returned | |
23 | + if one actually tries to edit an object. | |
24 | + Source: https://gist.github.com/aaugustin/1388243 | |
25 | + """ | |
26 | + | |
27 | + actions = None | |
28 | + | |
29 | + def get_readonly_fields(self, request, obj=None): | |
30 | + return self.fields or [f.name for f in self.model._meta.fields] | |
31 | + | |
32 | + def has_add_permission(self, request): | |
33 | + return False | |
34 | + | |
35 | + # Allow viewing objects but not actually changing them | |
36 | + def has_change_permission(self, request, obj=None): | |
37 | + if request.method not in ('GET', 'HEAD'): | |
38 | + return False | |
39 | + return super(ReadOnlyModelAdmin, self).has_change_permission(request, obj) | |
40 | + | |
41 | + def has_delete_permission(self, request, obj=None): | |
42 | + return False | |
43 | + | |
44 | + | |
45 | +# EP added | |
46 | + | |
47 | +# Edit mode | |
48 | +# DEBUG = False | |
49 | +# View only mode | |
50 | +# DEBUG = True | |
51 | + | |
52 | +''' Uncomment for production ''' | |
53 | + | |
54 | +# if settings.DEBUG: | |
55 | +# class PyrosModelAdmin(ReadOnlyModelAdmin): | |
56 | +# pass | |
57 | +# else: | |
58 | +# class PyrosModelAdmin(admin.ModelAdmin): | |
59 | +# pass | |
60 | + | |
61 | + | |
62 | +class PyrosModelAdmin(admin.ModelAdmin): | |
63 | + pass | |
64 | + | |
65 | +# Many To Many interface adapter | |
66 | + | |
67 | + | |
68 | +class PyrosUserAndSPInline(admin.TabularInline): | |
69 | + #model = ScientificProgram.pyros_users.through | |
70 | + pass | |
71 | + | |
72 | + | |
73 | +class SequenceAndScheduleInline(admin.TabularInline): | |
74 | + model = Schedule.sequences.through | |
75 | + | |
76 | +class PyrosUserAndUserLevelInline(admin.TabularInline): | |
77 | + # add admin representation for m2m relation between PyrosUser and UserLevel | |
78 | + model = UserLevel.pyros_users.through | |
79 | + | |
80 | +class ScheduleAdmin(admin.ModelAdmin): | |
81 | + inlines = [ | |
82 | + SequenceAndScheduleInline, | |
83 | + ] | |
84 | + exclude = ('sequences',) | |
85 | + | |
86 | + | |
87 | +# One To Many interface adapters | |
88 | + | |
89 | +class SequenceInline(admin.TabularInline): | |
90 | + model = Sequence | |
91 | + readonly_fields = ("name",) | |
92 | + fields = ("name",) | |
93 | + show_change_link = True | |
94 | + | |
95 | + | |
96 | +# class RequestInline(admin.TabularInline): | |
97 | +# model = Request | |
98 | +# readonly_fields = ("name",) | |
99 | +# fields = ("name",) | |
100 | +# show_change_link = True | |
101 | + | |
102 | + | |
103 | +class AlbumInline(admin.TabularInline): | |
104 | + model = Album | |
105 | + readonly_fields = ("name",) | |
106 | + fields = ("name",) | |
107 | + show_change_link = True | |
108 | + | |
109 | + | |
110 | +class PlanInline(admin.TabularInline): | |
111 | + model = Plan | |
112 | + #readonly_fields = ("name",) | |
113 | + #fields = ("name",) | |
114 | + show_change_link = True | |
115 | + | |
116 | + | |
117 | +class ImageInline(admin.TabularInline): | |
118 | + model = Image | |
119 | + readonly_fields = ("name",) | |
120 | + fields = ("name",) | |
121 | + show_change_link = True | |
122 | + | |
123 | + | |
124 | +class DetectorInline(admin.TabularInline): | |
125 | + model = Detector | |
126 | + readonly_fields = ("device_name",) | |
127 | + fields = ("device_name",) | |
128 | + show_change_link = True | |
129 | + | |
130 | + | |
131 | +class PyrosUserInline(admin.TabularInline): | |
132 | + model = PyrosUser | |
133 | + readonly_fields = ("user_username",) | |
134 | + fields = ("user_username",) | |
135 | + show_change_link = True | |
136 | + | |
137 | + | |
138 | +class FilterInline(admin.TabularInline): | |
139 | + model = Filter | |
140 | + readonly_fields = ("device_name",) | |
141 | + fields = ("device_name",) | |
142 | + show_change_link = True | |
143 | + | |
144 | + | |
145 | +# class AlertInline(admin.TabularInline): | |
146 | +# model = Alert | |
147 | +# readonly_fields = ("request_name",) | |
148 | +# fields = ("request_name",) | |
149 | +# show_change_link = True | |
150 | + | |
151 | + | |
152 | +# Admin model classes | |
153 | + | |
154 | +# class RequestAdmin(PyrosModelAdmin): | |
155 | +# pass | |
156 | + # inlines = [ | |
157 | + # SequenceInline, | |
158 | + # ] | |
159 | + | |
160 | + | |
161 | +class SequenceAdmin(PyrosModelAdmin): | |
162 | + inlines = [ | |
163 | + AlbumInline, | |
164 | + SequenceAndScheduleInline, # for M2M interface | |
165 | + ] | |
166 | + | |
167 | + | |
168 | +class PyrosUserAdmin(PyrosModelAdmin): | |
169 | + list_display = ("user_username","is_active","laboratory") | |
170 | + list_filter = ("is_active",) | |
171 | + list_editable = ("is_active",) | |
172 | + inlines = [ | |
173 | + #RequestInline, | |
174 | + # A user has many SPs | |
175 | +# PyrosUserAndSPInline, # for M2M interface | |
176 | + ] | |
177 | + | |
178 | + | |
179 | +''' | |
180 | +class StrategyObsAdmin(PyrosModelAdmin): | |
181 | + inlines = [ | |
182 | + #AlertInline, | |
183 | + ] | |
184 | +''' | |
185 | + | |
186 | + | |
187 | +class ScientificProgramAdmin(PyrosModelAdmin): | |
188 | + inlines = [ | |
189 | + #RequestInline, | |
190 | + # A SP has many users: | |
191 | + # PyrosUserAndSPInline, # for M2M interface | |
192 | + ] | |
193 | + exclude = ('pyros_users',) # for M2M interface | |
194 | + | |
195 | + | |
196 | +class CountryAdmin(PyrosModelAdmin): | |
197 | + inlines = [ | |
198 | + PyrosUserInline, | |
199 | + ] | |
200 | + | |
201 | + | |
202 | +class UserLevelAdmin(PyrosModelAdmin): | |
203 | + inlines = [ | |
204 | + #PyrosUserInline, | |
205 | + PyrosUserAndUserLevelInline, | |
206 | + ] | |
207 | + list_display = ("name","priority",) | |
208 | + # we need to exclude pyros_users which represents the m2m relation between UserLevel and PyrosUser | |
209 | + exclude = ("pyros_users",) | |
210 | + | |
211 | + | |
212 | +class FilterAdmin(PyrosModelAdmin): | |
213 | + # inlines = [ | |
214 | + # PlanInline, | |
215 | + # ] | |
216 | + pass | |
217 | + | |
218 | + | |
219 | +class FilterWheelAdmin(PyrosModelAdmin): | |
220 | + inlines = [ | |
221 | + FilterInline, | |
222 | + ] | |
223 | + | |
224 | + | |
225 | +''' | |
226 | +class NrtAnalysisAdmin(PyrosModelAdmin): | |
227 | + inlines = [ | |
228 | + ImageInline, | |
229 | + ] | |
230 | +''' | |
231 | + | |
232 | + | |
233 | +class DetectorAdmin(PyrosModelAdmin): | |
234 | + pass | |
235 | + # inlines = [ | |
236 | + # AlbumInline, | |
237 | + # ] | |
238 | + | |
239 | + | |
240 | +class TelescopeAdmin(PyrosModelAdmin): | |
241 | + inlines = [ | |
242 | + DetectorInline, | |
243 | + ] | |
244 | + | |
245 | + | |
246 | +class PlanAdmin(PyrosModelAdmin): | |
247 | + inlines = [ | |
248 | + ImageInline, | |
249 | + ] | |
250 | + | |
251 | + | |
252 | +# class AlbumAdmin(admin.ModelAdmin): | |
253 | +class AlbumAdmin(PyrosModelAdmin): | |
254 | + inlines = [ | |
255 | + PlanInline, | |
256 | + ] | |
257 | + | |
258 | + | |
259 | +# Link the models to the admin interface | |
260 | + | |
261 | +# (EP added 10/7/19) | |
262 | +admin.site.register(AgentCmd) | |
263 | +admin.site.register(AgentLogs) | |
264 | +admin.site.register(AgentSurvey) | |
265 | +admin.site.register(AgentDeviceStatus) | |
266 | + | |
2 | 267 | |
3 | -# Register your models here. | |
268 | +admin.site.register(Album, AlbumAdmin) | |
269 | +#admin.site.register(Alert) | |
270 | +admin.site.register(Country, CountryAdmin) | |
271 | +admin.site.register(Detector, DetectorAdmin) | |
272 | +admin.site.register(Filter, FilterAdmin) | |
273 | +admin.site.register(FilterWheel, FilterWheelAdmin) | |
274 | +admin.site.register(Image) | |
275 | +admin.site.register(Log) | |
276 | +#admin.site.register(NrtAnalysis, NrtAnalysisAdmin) | |
277 | +admin.site.register(Plan, PlanAdmin) | |
278 | +#admin.site.register(Request, RequestAdmin) | |
279 | +admin.site.register(Schedule, ScheduleAdmin) | |
280 | +admin.site.register(ScheduleHasSequences) | |
281 | +admin.site.register(ScientificProgram, ScientificProgramAdmin) | |
282 | +admin.site.register(Sequence, SequenceAdmin) | |
283 | +admin.site.register(SiteWatch) | |
284 | +admin.site.register(SiteWatchHistory) | |
285 | +#admin.site.register(StrategyObs, StrategyObsAdmin) | |
286 | +##admin.site.register(TaskId) | |
287 | +admin.site.register(Telescope, TelescopeAdmin) | |
288 | +admin.site.register(PyrosUser, PyrosUserAdmin) | |
289 | +admin.site.register(UserLevel, UserLevelAdmin) | |
290 | +admin.site.register(Version) | |
291 | +admin.site.register(WeatherWatch) | |
292 | +admin.site.register(WeatherWatchHistory) | |
293 | +admin.site.register(PlcDeviceStatus) | |
294 | +admin.site.register(PlcDevice) | |
295 | +admin.site.register(Config) | |
296 | +admin.site.register(Institute) | |
4 | 297 | \ No newline at end of file | ... | ... |
src/core/pyros_django/dashboard/models.py
1 | +##from __future__ import unicode_literals | |
2 | + | |
3 | +# (EP 21/9/22) To allow autoreferencing (ex: AgentCmd.create() returns a AgentCmd) | |
4 | +from __future__ import annotations | |
5 | + | |
6 | +# Stdlib imports | |
7 | +from src.device_controller.abstract_component.device_controller import DeviceCmd | |
8 | +from datetime import datetime, timedelta, date | |
9 | +from dateutil.relativedelta import relativedelta | |
10 | +import os | |
11 | +import sys | |
12 | +from typing import Any, List, Tuple, Optional | |
13 | +import re | |
14 | + | |
15 | +# Django imports | |
16 | +from django.core.validators import MaxValueValidator, MinValueValidator | |
17 | + | |
18 | +# DJANGO imports | |
19 | +from django.contrib.auth.models import AbstractUser, UserManager | |
1 | 20 | from django.db import models |
21 | +from django.db.models import Q, Max | |
22 | +from django.core.validators import MaxValueValidator, MinValueValidator | |
23 | +#from django.db.models.deletion import DO_NOTHING | |
24 | +from django.db.models.expressions import F | |
25 | +from django.db.models.query import QuerySet | |
26 | +from model_utils import Choices | |
27 | +from django.utils import timezone | |
28 | +from asgiref.sync import async_to_sync | |
29 | +from channels.layers import get_channel_layer | |
30 | +from django.db.models.signals import post_save | |
31 | +from django.dispatch import receiver | |
32 | +# Project imports | |
33 | +from user_mgmt.models import PyrosUser | |
34 | +# DeviceCommand is used by class Command | |
35 | +sys.path.append("../../..") | |
36 | + | |
37 | + | |
38 | + | |
39 | + | |
40 | +""" | |
41 | +STYLE RULES | |
42 | +=========== | |
43 | +https://simpleisbetterthancomplex.com/tips/2018/02/10/django-tip-22-designing-better-models.html | |
44 | +https://steelkiwi.com/blog/best-practices-working-django-models-python/ | |
45 | + | |
46 | +- Model name => singular | |
47 | + Call it Company instead of Companies. | |
48 | + A model definition is the representation of a single object (the object in this example is a company), | |
49 | + and not a collection of companies | |
50 | + The model definition is a class, so always use CapWords convention (no underscores) | |
51 | + E.g. User, Permission, ContentType, etc. | |
52 | + | |
53 | +- For the model’s attributes use snake_case. | |
54 | + E.g. first_name, last_name, etc | |
55 | + | |
56 | +- Blank and Null Fields (https://simpleisbetterthancomplex.com/tips/2016/07/25/django-tip-8-blank-or-null.html) | |
57 | + - Null: It is database-related. Defines if a given database column will accept null values or not. | |
58 | + - Blank: It is validation-related. It will be used during forms validation, when calling form.is_valid(). | |
59 | + Do not use null=True for text-based fields that are optional. | |
60 | + Otherwise, you will end up having two possible values for “no data”, that is: None and an empty string. | |
61 | + Having two possible values for “no data” is redundant. | |
62 | + The Django convention is to use the empty string, not NULL. | |
63 | + Example: | |
64 | + # The default values of `null` and `blank` are `False`. | |
65 | + class Person(models.Model): | |
66 | + name = models.CharField(max_length=255) # Mandatory | |
67 | + bio = models.TextField(max_length=500, blank=True) # Optional (don't put null=True) | |
68 | + birth_date = models.DateField(null=True, blank=True) # Optional (here you may add null=True) | |
69 | + The default values of null and blank are False. | |
70 | + Special case, when you need to accept NULL values for a BooleanField, use NullBooleanField instead. | |
71 | + | |
72 | +- Choices : you can use Choices from the model_utils library. Take model Article, for instance: | |
73 | + from model_utils import Choices | |
74 | + class Article(models.Model): | |
75 | + STATUSES = Choices( | |
76 | + (0, 'draft', _('draft')), | |
77 | + (1, 'published', _('published')) ) | |
78 | + status = models.IntegerField(choices=STATUSES, default=STATUSES.draft) | |
79 | + | |
80 | +- Reverse Relationships | |
81 | + | |
82 | + - related_name : | |
83 | + Rule of thumb: if you are not sure what would be the related_name, | |
84 | + use the plural of the model holding the ForeignKey. | |
85 | + ex: | |
86 | + class Company: | |
87 | + name = models.CharField(max_length=30) | |
88 | + class Employee: | |
89 | + first_name = models.CharField(max_length=30) | |
90 | + last_name = models.CharField(max_length=30) | |
91 | + company = models.ForeignKey(Company, on_delete=models.CASCADE, related_name='employees') | |
92 | + usage: | |
93 | + google = Company.objects.get(name='Google') | |
94 | + google.employees.all() | |
95 | + You can also use the reverse relationship to modify the company field on the Employee instances: | |
96 | + vitor = Employee.objects.get(first_name='Vitor') | |
97 | + google = Company.objects.get(name='Google') | |
98 | + google.employees.add(vitor) | |
99 | + | |
100 | + - related_query_name : | |
101 | + This kind of relationship also applies to query filters. | |
102 | + For example, if I wanted to list all companies that employs people named ‘Vitor’, I could do the following: | |
103 | + companies = Company.objects.filter(employee__first_name='Vitor') | |
104 | + If you want to customize the name of this relationship, here is how we do it: | |
105 | + class Employee: | |
106 | + first_name = models.CharField(max_length=30) | |
107 | + last_name = models.CharField(max_length=30) | |
108 | + company = models.ForeignKey( | |
109 | + Company, | |
110 | + on_delete=models.CASCADE, | |
111 | + related_name='employees', | |
112 | + related_query_name='person' | |
113 | + ) | |
114 | + Then the usage would be: | |
115 | + companies = Company.objects.filter(person__first_name='Vitor') | |
116 | + | |
117 | + To use it consistently, related_name goes as plural and related_query_name goes as singular. | |
118 | + | |
119 | + | |
120 | +GENERAL EXAMPLE | |
121 | +======= | |
122 | + | |
123 | +from django.db import models | |
124 | +from django.urls import reverse | |
125 | + | |
126 | +class Company(models.Model): | |
127 | + # CHOICES | |
128 | + PUBLIC_LIMITED_COMPANY = 'PLC' | |
129 | + PRIVATE_COMPANY_LIMITED = 'LTD' | |
130 | + LIMITED_LIABILITY_PARTNERSHIP = 'LLP' | |
131 | + COMPANY_TYPE_CHOICES = ( | |
132 | + (PUBLIC_LIMITED_COMPANY, 'Public limited company'), | |
133 | + (PRIVATE_COMPANY_LIMITED, 'Private company limited by shares'), | |
134 | + (LIMITED_LIABILITY_PARTNERSHIP, 'Limited liability partnership'), | |
135 | + ) | |
136 | + | |
137 | + # DATABASE FIELDS | |
138 | + name = models.CharField('name', max_length=30) | |
139 | + vat_identification_number = models.CharField('VAT', max_length=20) | |
140 | + company_type = models.CharField('type', max_length=3, choices=COMPANY_TYPE_CHOICES) | |
141 | + | |
142 | + # MANAGERS | |
143 | + objects = models.Manager() | |
144 | + limited_companies = LimitedCompanyManager() | |
145 | + | |
146 | + # META CLASS | |
147 | + class Meta: | |
148 | + verbose_name = 'company' | |
149 | + verbose_name_plural = 'companies' | |
150 | + | |
151 | + # TO STRING METHOD | |
152 | + def __str__(self): | |
153 | + return self.name | |
154 | + | |
155 | + # SAVE METHOD | |
156 | + def save(self, *args, **kwargs): | |
157 | + do_something() | |
158 | + super().save(*args, **kwargs) # Call the "real" save() method. | |
159 | + do_something_else() | |
160 | + | |
161 | + # ABSOLUTE URL METHOD | |
162 | + def get_absolute_url(self): | |
163 | + return reverse('company_details', kwargs={'pk': self.id}) | |
164 | + | |
165 | + # OTHER METHODS | |
166 | + def process_invoices(self): | |
167 | + do_something() | |
168 | + | |
169 | +""" | |
170 | + | |
171 | + | |
172 | +# --- | |
173 | +# --- Utility functions | |
174 | +# --- | |
175 | + | |
176 | +def printd(*args, **kwargs): | |
177 | + if os.environ.get('PYROS_DEBUG', '0') == '1': | |
178 | + print('(MODEL)', *args, **kwargs) | |
179 | + | |
180 | +''' | |
181 | +def get_or_create_unique_row_from_model(model: models.Model): | |
182 | + # return model.objects.get(id=1) if model.objects.exists() else model.objects.create(id=1) | |
183 | + return model.objects.first() if model.objects.exists() else model.objects.create(id=1) | |
184 | +''' | |
185 | + | |
186 | + | |
187 | + | |
188 | + | |
189 | + | |
190 | +""" | |
191 | +------------------------ | |
192 | + BASE MODEL CLASSES | |
193 | +------------------------ | |
194 | +""" | |
195 | + | |
196 | +""" | |
197 | +------------------------ | |
198 | + OTHER MODEL CLASSES | |
199 | +------------------------ | |
200 | +""" | |
201 | + | |
202 | + | |
203 | +# TODO: OLD Config : à virer (mais utilisé dans dashboard/templatetags/tags.py) | |
204 | +class Config(models.Model): | |
205 | + PYROS_STATE = ["Starting", "Passive", "Standby", | |
206 | + "Remote", "Startup", "Scheduler", "Closing"] | |
207 | + | |
208 | + id = models.IntegerField(default='1', primary_key=True) | |
209 | + #latitude = models.FloatField(default=1) | |
210 | + latitude = models.DecimalField( | |
211 | + max_digits=4, decimal_places=2, | |
212 | + default=1, | |
213 | + validators=[ | |
214 | + MaxValueValidator(90), | |
215 | + MinValueValidator(-90) | |
216 | + ] | |
217 | + ) | |
218 | + local_time_zone = models.FloatField(default=1) | |
219 | + #longitude = models.FloatField(default=1) | |
220 | + longitude = models.DecimalField( | |
221 | + max_digits=5, decimal_places=2, | |
222 | + default=1, | |
223 | + validators=[ | |
224 | + MaxValueValidator(360), | |
225 | + MinValueValidator(-360) | |
226 | + ] | |
227 | + ) | |
228 | + altitude = models.FloatField(default=1) | |
229 | + horizon_line = models.FloatField(default=1) | |
230 | + row_data_save_frequency = models.IntegerField(default='300') | |
231 | + request_frequency = models.IntegerField(default='300') | |
232 | + analysed_data_save = models.IntegerField(default='300') | |
233 | + telescope_ip_address = models.CharField(max_length=45, default="127.0.0.1") | |
234 | + camera_ip_address = models.CharField(max_length=45, default="127.0.0.1") | |
235 | + plc_ip_address = models.CharField(max_length=45, default="127.0.0.1") | |
236 | + | |
237 | + # TODO: changer ça, c'est pas clair du tout... | |
238 | + # True = mode Scheduler-standby, False = mode Remote !!!! | |
239 | + global_mode = models.BooleanField(default='True') | |
240 | + | |
241 | + ack = models.BooleanField(default='False') | |
242 | + bypass = models.BooleanField(default='True') | |
243 | + lock = models.BooleanField(default='False') | |
244 | + pyros_state = models.CharField(max_length=25, default=PYROS_STATE[0]) | |
245 | + force_passive_mode = models.BooleanField(default='False') | |
246 | + plc_timeout_seconds = models.PositiveIntegerField(default=60) | |
247 | + majordome_state = models.CharField(max_length=25, default="") | |
248 | + ntc = models.BooleanField(default='False') | |
249 | + majordome_restarted = models.BooleanField(default='False') | |
250 | + | |
251 | + class Meta: | |
252 | + managed = True | |
253 | + db_table = 'config' | |
254 | + verbose_name_plural = "Config" | |
255 | + | |
256 | + def __str__(self): | |
257 | + return (str(self.__dict__)) | |
258 | + | |
259 | + | |
260 | + | |
261 | +class Log(models.Model): | |
262 | + agent = models.CharField(max_length=45, blank=True, null=True) | |
263 | + created = models.DateTimeField(blank=True, null=True, auto_now_add=True) | |
264 | + message = models.TextField(blank=True, null=True) | |
265 | + | |
266 | + class Meta: | |
267 | + managed = True | |
268 | + db_table = 'log' | |
269 | + | |
270 | + def __str__(self): | |
271 | + return (str(self.agent)) | |
272 | + | |
273 | + | |
274 | +# TODO: à virer car utilisé pour Celery (ou bien à utiliser pour les agents) | |
275 | +''' | |
276 | +class TaskId(models.Model): | |
277 | + task = models.CharField(max_length=45, blank=True, null=True) | |
278 | + created = models.DateTimeField(blank=True, null=True, auto_now_add=True) | |
279 | + task_id = models.CharField(max_length=45, blank=True, null=True) | |
280 | + | |
281 | + class Meta: | |
282 | + managed = True | |
283 | + db_table = 'task_id' | |
284 | + | |
285 | + def __str__(self): | |
286 | + return (str(self.task) + " - " + str(self.task_id)) | |
287 | +''' | |
288 | + | |
289 | + | |
290 | +class Version(models.Model): | |
291 | + module_name = models.CharField(max_length=45, blank=True, null=True) | |
292 | + version = models.CharField(max_length=15, blank=True, null=True) | |
293 | + created = models.DateTimeField(blank=True, null=True, auto_now_add=True) | |
294 | + updated = models.DateTimeField(blank=True, null=True, auto_now=True) | |
295 | + | |
296 | + class Meta: | |
297 | + managed = True | |
298 | + db_table = 'version' | |
299 | + | |
300 | + def __str__(self): | |
301 | + return (str(self.module_name) + " - " + str(self.version)) | |
302 | + | |
303 | + | |
304 | +class Tickets(models.Model): | |
305 | + created = models.DateTimeField(blank=True, null=True, auto_now_add=True) | |
306 | + updated = models.DateTimeField(blank=True, null=True, auto_now=True) | |
307 | + end = models.DateTimeField(blank=True, null=True) | |
308 | + title = models.TextField(blank=True, null=True) | |
309 | + description = models.TextField(blank=True, null=True) | |
310 | + resolution = models.TextField(blank=True, null=True) | |
311 | + pyros_user = models.ForeignKey(PyrosUser, on_delete=models.DO_NOTHING, related_name="tickets", blank=True, null=True) | |
312 | + last_modified_by = models.ForeignKey(PyrosUser, on_delete=models.DO_NOTHING, related_name="tickets_modified_by", blank=True, null=True) | |
313 | + LEVEL_ONE = "ONE" | |
314 | + LEVEL_TWO = "TWO" | |
315 | + LEVEL_THREE = "THREE" | |
316 | + LEVEL_FOUR = "FOUR" | |
317 | + LEVEL_FIVE = "FIVE" | |
318 | + SECURITY_LEVEL_CHOICES = ( | |
319 | + (LEVEL_ONE,"Warning non compromising for the operation of the system"), | |
320 | + (LEVEL_TWO,"Known issue which can be solved by operating the software remotely"), | |
321 | + (LEVEL_THREE,"Known issue which can be solved by an human remotely"), | |
322 | + (LEVEL_FOUR,"Known issue without immediate solution"), | |
323 | + (LEVEL_FIVE,"Issue not categorized until it happened") | |
324 | + ) | |
325 | + security_level = models.TextField(choices=SECURITY_LEVEL_CHOICES, default=LEVEL_ONE) | |
326 | + | |
327 | + class Meta: | |
328 | + managed = True | |
329 | + db_table = 'tickets' | |
330 | + verbose_name_plural = "tickets" | |
331 | + | |
2 | 332 | |
3 | -# Create your models here. | ... | ... |