server/python/django2/renkanmanager/models.py
changeset 614 23416a833ca8
parent 611 f0f07e2b841f
child 615 f3875fbe206a
equal deleted inserted replaced
613:e00a24b711a0 614:23416a833ca8
    15 
    15 
    16 logger = logging.getLogger(__name__)
    16 logger = logging.getLogger(__name__)
    17 auth_user_model = getattr(settings, 'AUTH_USER_MODEL', 'auth.User')
    17 auth_user_model = getattr(settings, 'AUTH_USER_MODEL', 'auth.User')
    18 
    18 
    19 class Workspace(models.Model):
    19 class Workspace(models.Model):
    20     
    20 
    21     workspace_guid = models.CharField(max_length=1024, default=uuid.uuid4, unique=True, blank=False, null=False) # typically UUID
    21     workspace_guid = models.UUIDField(default=uuid.uuid4, editable=False, unique=True, blank=False, null=False)
    22     title = models.CharField(max_length=1024, null=True)
    22     title = models.CharField(max_length=1024, null=True)
    23     creator = models.ForeignKey(auth_user_model, blank=True, null=True, related_name="workspace_creator")
    23     creator = models.ForeignKey(auth_user_model, blank=True, null=True, related_name="workspace_creator")
    24     creation_date = models.DateTimeField(auto_now_add=True)
    24     creation_date = models.DateTimeField(auto_now_add=True)
    25     
    25 
    26     @property
    26     @property
    27     def renkan_count(self):
    27     def renkan_count(self):
    28         return Renkan.objects.filter(workspace__workspace_guid=self.workspace_guid).count()
    28         #TODO: check count and related objects
    29     
    29         #return Renkan.objects.filter(workspace__workspace_guid=self.workspace_guid).count()
       
    30         return Renkan.objects.filter(workspace_guid=self.workspace_guid).count()
       
    31 
    30     class Meta:
    32     class Meta:
    31         app_label = 'renkanmanager'
    33         app_label = 'renkanmanager'
    32         permissions = (
    34         permissions = (
    33             ('view_workspace', 'Can view workspace'),
    35             ('view_workspace', 'Can view workspace'),
    34         )
    36         )
    35         
    37 
    36 
    38 
    37 class RenkanManager(models.Manager):
    39 class RenkanManager(models.Manager):
    38     
    40 
    39     @transaction.atomic
    41     @transaction.atomic
    40     def create_renkan(self, creator, title='', content='', source_revision=None, workspace = None):
    42     def create_renkan(self, creator, title='', content='', source_revision=None, workspace = None):
    41         new_renkan = Renkan()
    43         new_renkan = Renkan()
    42         new_renkan.creator = creator
    44         new_renkan.creator = creator
       
    45         #TODO: !!! new_renkan_workspace_guid is not set on the new renkan ! only on the content !
    43         new_renkan_workspace_guid = ""
    46         new_renkan_workspace_guid = ""
    44         new_renkan_title = title
    47         new_renkan_title = title
    45         new_renkan_content = content
    48         new_renkan_content = content
    46         if workspace is not None:
    49         if workspace is not None:
    47             new_renkan.workspace = workspace
    50             new_renkan.workspace = workspace
    62         initial_revision.title = new_renkan_title if new_renkan_title else "Untitled Renkan"
    65         initial_revision.title = new_renkan_title if new_renkan_title else "Untitled Renkan"
    63         if new_renkan_content:
    66         if new_renkan_content:
    64             new_renkan_content_dict = json.loads(new_renkan.validate_json_content(new_renkan_content))
    67             new_renkan_content_dict = json.loads(new_renkan.validate_json_content(new_renkan_content))
    65             new_renkan_content_dict["created"] = str(initial_revision.creation_date)
    68             new_renkan_content_dict["created"] = str(initial_revision.creation_date)
    66             new_renkan_content_dict["updated"] = str(initial_revision.modification_date)
    69             new_renkan_content_dict["updated"] = str(initial_revision.modification_date)
    67         else: 
    70         else:
    68             new_renkan_content_dict = {
    71             new_renkan_content_dict = {
    69                 "id": str(new_renkan.renkan_guid),
    72                 "id": str(new_renkan.renkan_guid),
    70                 "title": initial_revision.title,
    73                 "title": initial_revision.title,
    71                 "description": "",
    74                 "description": "",
    72                 "created": str(initial_revision.creation_date),
    75                 "created": str(initial_revision.creation_date),
    78                 "views": []
    81                 "views": []
    79             }
    82             }
    80         initial_revision.content = json.dumps(new_renkan_content_dict)
    83         initial_revision.content = json.dumps(new_renkan_content_dict)
    81         initial_revision.save()
    84         initial_revision.save()
    82         return new_renkan
    85         return new_renkan
    83     
    86 
    84 class Renkan(models.Model):
    87 class Renkan(models.Model):
    85     
    88 
    86     renkan_guid = models.CharField(max_length=256, default=uuid.uuid4, unique=True, blank=False, null=False) # typically UUID
    89     renkan_guid = models.UUIDField(default=uuid.uuid4, editable=False, unique=True, blank=False, null=False)
    87     workspace = models.ForeignKey('Workspace', null=True, blank=True, to_field='workspace_guid')
    90     workspace_guid = models.CharField(max_length=256, blank=True, null=True)
    88     source_revision = models.ForeignKey('Revision', null=True, blank=True, related_name="renkan_source_revision", to_field='revision_guid', on_delete=models.SET_NULL)
    91     current_revision_guid = models.CharField(max_length=256, blank=True, null=True)
       
    92     source_revision_guid = models.CharField(max_length=256, blank=True, null=True)
       
    93 
    89     creator = models.ForeignKey(auth_user_model, blank=True, null=True, related_name="renkan_creator")
    94     creator = models.ForeignKey(auth_user_model, blank=True, null=True, related_name="renkan_creator")
    90     creation_date = models.DateTimeField(auto_now_add=True)
    95     creation_date = models.DateTimeField(auto_now_add=True)
    91     state = models.IntegerField(default=1)
    96     state = models.IntegerField(default=1)
    92     
    97 
    93     objects = RenkanManager()
    98     objects = RenkanManager()
    94     
    99 
    95     @property
   100     @property
    96     def revision_count(self):
   101     def revision_count(self):
    97         return Revision.objects.filter(parent_renkan__renkan_guid=self.renkan_guid).count()
   102         #TODO: check related object count
    98     
   103         return Revision.objects.filter(parent_renkan_guid=self.renkan_guid).count()
       
   104         #return Revision.objects.filter(parent_renkan__renkan_guid=self.renkan_guid).count()
       
   105 
    99     @property
   106     @property
   100     def is_copy(self):
   107     def is_copy(self):
   101         return bool(self.source_revision)
   108         #return bool(self.source_revision)
   102     
   109         return bool(self.source_revision_guid)
       
   110 
   103     # Current revision object or None if there is none
   111     # Current revision object or None if there is none
   104     @property
   112 #    @property
   105     def current_revision(self):
   113 #    def current_revision(self):
   106         return Revision.objects.filter(parent_renkan__renkan_guid=self.renkan_guid).order_by('-creation_date').first()
   114 #        return Revision.objects.filter(parent_renkan__renkan_guid=self.renkan_guid).order_by('-creation_date').first()
   107     
   115 
   108     # Current revision title
   116     # Current revision title
   109     @property
   117     @property
   110     def title(self):
   118     def title(self):
   111         if self.current_revision:
   119         current_revision = Revision.objects.get(revision_guid = self.current_revision_guid)
   112             return self.current_revision.title
   120         return current_revision.title
   113         else:
   121         #TODO: not good -> 2 requests
   114             return ''
   122         #if self.current_revision:
   115     
   123         #    return self.current_revision.title
       
   124         #else:
       
   125         #    return ''
       
   126 
   116     # Current revision content
   127     # Current revision content
   117     @property
   128     @property
   118     def content(self):
   129     def content(self):
   119         if self.current_revision:
   130         #TODO: not good -> 2 requests
   120             return self.current_revision.content
   131         current_revision = Revision.objects.get(revision_guid = self.current_revision_guid)
   121         else:
   132         return current_revision.content
   122             return ''
   133         #if self.current_revision:
   123     
   134         #    return self.current_revision.content
       
   135         #else:
       
   136         #    return ''
       
   137 
   124     def __unicode__(self):
   138     def __unicode__(self):
   125         return self.renkan_guid
   139         return self.renkan_guid
   126     
   140 
   127     def __str__(self):
   141     def __str__(self):
   128         return self.renkan_guid
   142         return self.renkan_guid
   129     
   143 
   130     @transaction.atomic
   144     @transaction.atomic
   131     def save_renkan(self, updator, timestamp="", title="", content="", create_new_revision=False):
   145     def save_renkan(self, updator, timestamp="", title="", content="", create_new_revision=False):
   132         """
   146         """
   133             Saves over current revision or saves a new revision entirely. 
   147             Saves over current revision or saves a new revision entirely.
   134             Timestamp must be the current revision modification_date.
   148             Timestamp must be the current revision modification_date.
   135         """
   149         """
   136         if (not timestamp) or ((self.current_revision is not None) and dateparse.parse_datetime(timestamp) < self.current_revision.modification_date):
   150         if (not timestamp) or ((self.current_revision is not None) and dateparse.parse_datetime(timestamp) < self.current_revision.modification_date):
   137             logger.error("SAVING RENKAN: provided timestamp is %r, which isn't current revision modification_date %r", timestamp, self.current_revision.modification_date)
   151             logger.error("SAVING RENKAN: provided timestamp is %r, which isn't current revision modification_date %r", timestamp, self.current_revision.modification_date)
   138             raise ValidationError(_("Cannot save, provided timestamp is invalid"))
   152             raise ValidationError(_("Cannot save, provided timestamp is invalid"))
   142         if create_new_revision:
   156         if create_new_revision:
   143             revision_to_update = Revision(parent_renkan=self)
   157             revision_to_update = Revision(parent_renkan=self)
   144             revision_to_update.creator = updator
   158             revision_to_update.creator = updator
   145         else:
   159         else:
   146             revision_to_update = Revision.objects.select_for_update().get(revision_guid=self.current_revision.revision_guid)
   160             revision_to_update = Revision.objects.select_for_update().get(revision_guid=self.current_revision.revision_guid)
   147         
   161 
   148         updated_content = self.validate_json_content(content) if content else current_revision.content
   162         updated_content = self.validate_json_content(content) if content else current_revision.content
   149         updated_content_dict = json.loads(updated_content)
   163         updated_content_dict = json.loads(updated_content)
   150         
   164 
   151         # If title is passed as arg to the method, update the title in the json
   165         # If title is passed as arg to the method, update the title in the json
   152         if title:
   166         if title:
   153             updated_title = title
   167             updated_title = title
   154             updated_content_dict["title"] = title
   168             updated_content_dict["title"] = title
   155         # If it is not, we use the one in the json instead
   169         # If it is not, we use the one in the json instead
   156         else:
   170         else:
   157             updated_title = updated_content_dict["title"] 
   171             updated_title = updated_content_dict["title"]
   158         
   172 
   159         revision_to_update.modification_date = timezone.now()
   173         revision_to_update.modification_date = timezone.now()
   160         updated_content_dict["updated"] = str(revision_to_update.modification_date)
   174         updated_content_dict["updated"] = str(revision_to_update.modification_date)
   161         updated_content = json.dumps(updated_content_dict)  
   175         updated_content = json.dumps(updated_content_dict)
   162         revision_to_update.title = updated_title
   176         revision_to_update.title = updated_title
   163         revision_to_update.content = updated_content
   177         revision_to_update.content = updated_content
   164         if dt_timestamp == revision_to_update.modification_date:
   178         if dt_timestamp == revision_to_update.modification_date:
   165             revision_to_update.modification_date += datetime.resolution
   179             revision_to_update.modification_date += datetime.resolution
   166         revision_to_update.last_updated_by = updator
   180         revision_to_update.last_updated_by = updator
   167         revision_to_update.save()
   181         revision_to_update.save()
   168     
   182 
   169     def validate_json_content(self, content):
   183     def validate_json_content(self, content):
   170         """
   184         """
   171             Checks that the json content is valid (keys and structures), raise a ValidationError if format is wrong or value is wrong (for ids),
   185             Checks that the json content is valid (keys and structures), raise a ValidationError if format is wrong or value is wrong (for ids),
   172             if a key is missing, autocompletes with the empty default value
   186             if a key is missing, autocompletes with the empty default value
   173             
   187 
   174             Returns the validated json string
   188             Returns the validated json string
   175         """
   189         """
   176         try:
   190         try:
   177             content_to_validate_dict = json.loads(content)
   191             content_to_validate_dict = json.loads(content)
   178         except ValueError:
   192         except ValueError:
   196             content_to_validate_dict["edges"] = []
   210             content_to_validate_dict["edges"] = []
   197         if "views" not in content_to_validate_dict:
   211         if "views" not in content_to_validate_dict:
   198             content_to_validate_dict["views"] = []
   212             content_to_validate_dict["views"] = []
   199         if "users" not in content_to_validate_dict:
   213         if "users" not in content_to_validate_dict:
   200             content_to_validate_dict["users"] = []
   214             content_to_validate_dict["users"] = []
   201         
   215 
   202         if type(content_to_validate_dict["nodes"]) is not list:
   216         if type(content_to_validate_dict["nodes"]) is not list:
   203             raise ValidationError("Provided content has an invalid 'nodes' key: not a list")
   217             raise ValidationError("Provided content has an invalid 'nodes' key: not a list")
   204         if type(content_to_validate_dict["edges"]) is not list:
   218         if type(content_to_validate_dict["edges"]) is not list:
   205             raise ValidationError("Provided content has an invalid 'edges' key: not a list")
   219             raise ValidationError("Provided content has an invalid 'edges' key: not a list")
   206         if type(content_to_validate_dict["views"]) is not list:
   220         if type(content_to_validate_dict["views"]) is not list:
   207             raise ValidationError("Provided content has an invalid 'views' key: not a list")
   221             raise ValidationError("Provided content has an invalid 'views' key: not a list")
   208         if type(content_to_validate_dict["users"]) is not list:
   222         if type(content_to_validate_dict["users"]) is not list:
   209             raise ValidationError("Provided content has an invalid 'users' key: not a list")
   223             raise ValidationError("Provided content has an invalid 'users' key: not a list")
   210         return json.dumps(content_to_validate_dict)
   224         return json.dumps(content_to_validate_dict)
   211         
   225 
   212     @transaction.atomic
   226     #TODO:
   213     def delete(self):
   227     # @transaction.atomic
   214         """
   228     # def delete(self):
   215             Deleting a renkan also deletes every related revision
   229     #     """
   216         """
   230     #         Deleting a renkan also deletes every related revision
   217         renkan_revisions = Revision.objects.filter(parent_renkan__renkan_guid = self.renkan_guid)
   231     #     """
   218         for child_revision in renkan_revisions:
   232     #     renkan_revisions = Revision.objects.filter(parent_renkan__renkan_guid = self.renkan_guid)
   219             child_revision.delete()
   233     #     for child_revision in renkan_revisions:
   220         super(Renkan, self).delete()
   234     #         child_revision.delete()
   221         
   235     #     super(Renkan, self).delete()
       
   236 
   222     class Meta:
   237     class Meta:
   223         app_label = 'renkanmanager'
   238         app_label = 'renkanmanager'
   224         permissions = (
   239         permissions = (
   225             ('view_renkan', 'Can view renkan'),
   240             ('view_renkan', 'Can view renkan'),
   226         )
   241         )
   227 
   242 
   228         
   243 
   229 class Revision(models.Model):
   244 class Revision(models.Model):
   230     
   245 
   231     revision_guid = models.CharField(max_length=256, default=uuid.uuid4, unique=True) # typically UUID
   246     revision_guid = models.UUIDField(default=uuid.uuid4, editable=False, unique=True, blank=False, null=False)
   232     parent_renkan = models.ForeignKey('Renkan', null=False, blank=False, to_field='renkan_guid')
   247     parent_renkan_guid = models.CharField(max_length=256)
       
   248     #parent_renkan = models.ForeignKey('Renkan', null=False, blank=False, to_field='renkan_guid')
   233     title = models.CharField(max_length=1024, null=True, blank=True)
   249     title = models.CharField(max_length=1024, null=True, blank=True)
   234     content = models.TextField(blank=True, null=True)
   250     content = models.TextField(blank=True, null=True)
   235     creator = models.ForeignKey(auth_user_model, blank=True, null=True, related_name="revision_creator")
   251     creator = models.ForeignKey(auth_user_model, blank=True, null=True, related_name="revision_creator")
   236     last_updated_by = models.ForeignKey(auth_user_model, blank=True, null=True, related_name="revision_last_updated_by")
   252     last_updated_by = models.ForeignKey(auth_user_model, blank=True, null=True, related_name="revision_last_updated_by")
   237     creation_date = models.DateTimeField(auto_now_add=True)
   253     creation_date = models.DateTimeField(auto_now_add=True)
   238     modification_date = models.DateTimeField()
   254     modification_date = models.DateTimeField(auto_now=True)
   239     
   255     #modification_date = models.DateTimeField()
       
   256 
   240     @property
   257     @property
   241     def is_current_revision(self):
   258     def is_current_revision(self):
       
   259         try:
       
   260             parent_project = Renkan.objects.get(renkan_guid=self.parent_renkan_guid)
       
   261         except Renkan.DoesNotExist: # SHOULD NOT HAPPEN!
       
   262             raise Http404
       
   263         return parent_project.current_revision_guid == self.revision_guid
   242         # No need to check if parent_renkan.current_revision is not None, as it won't be if we're calling from a revision
   264         # No need to check if parent_renkan.current_revision is not None, as it won't be if we're calling from a revision
   243         return self.parent_renkan.current_revision.revision_guid == self.revision_guid
   265         #return self.parent_renkan.current_revision.revision_guid == self.revision_guid
   244     
   266 
   245     class Meta:
   267     class Meta:
   246         app_label = 'renkanmanager'
   268         app_label = 'renkanmanager'
   247         permissions = (
   269         permissions = (
   248             ('view_revision', 'Can view revision'),
   270             ('view_revision', 'Can view revision'),
   249         )       
   271         )
   250