server/python/django2/renkanmanager/models.py
changeset 615 f3875fbe206a
parent 614 23416a833ca8
child 616 33fdb6f8164c
equal deleted inserted replaced
614:23416a833ca8 615:f3875fbe206a
    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         #TODO: check count and related objects
    28         return self.renkans.all().count()
    29         #return Renkan.objects.filter(workspace__workspace_guid=self.workspace_guid).count()
       
    30         return Renkan.objects.filter(workspace_guid=self.workspace_guid).count()
       
    31 
    29 
    32     class Meta:
    30     class Meta:
    33         app_label = 'renkanmanager'
    31         app_label = 'renkanmanager'
    34         permissions = (
    32         permissions = (
    35             ('view_workspace', 'Can view workspace'),
    33             ('view_workspace', 'Can view workspace'),
    40 
    38 
    41     @transaction.atomic
    39     @transaction.atomic
    42     def create_renkan(self, creator, title='', content='', source_revision=None, workspace = None):
    40     def create_renkan(self, creator, title='', content='', source_revision=None, workspace = None):
    43         new_renkan = Renkan()
    41         new_renkan = Renkan()
    44         new_renkan.creator = creator
    42         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 = None
    46         new_renkan_workspace_guid = ""
       
    47         new_renkan_title = title
    44         new_renkan_title = title
    48         new_renkan_content = content
    45         new_renkan_content = content
    49         if workspace is not None:
    46         if workspace is not None:
    50             new_renkan.workspace = workspace
    47             new_renkan.workspace = workspace
    51             new_renkan_workspace_guid = workspace.workspace_guid
    48             new_renkan_workspace_guid = workspace.workspace_guid
    53             new_renkan.source_revision = source_revision
    50             new_renkan.source_revision = source_revision
    54             if not title:
    51             if not title:
    55                 new_renkan_title = source_revision.title
    52                 new_renkan_title = source_revision.title
    56             new_renkan_content = source_revision.content
    53             new_renkan_content = source_revision.content
    57         new_renkan.save()
    54         new_renkan.save()
       
    55         creation_date =  timezone.now()
    58         initial_revision = Revision(parent_renkan=new_renkan)
    56         initial_revision = Revision(parent_renkan=new_renkan)
    59         initial_revision.modification_date = timezone.now()
    57         initial_revision.title = new_renkan_title if new_renkan_title else "Untitled Renkan"
       
    58         initial_revision.creation_date = creation_date
       
    59         initial_revision.modification_date = creation_date
    60         initial_revision.creator = creator
    60         initial_revision.creator = creator
    61         initial_revision.last_updated_by = creator
    61         initial_revision.last_updated_by = creator
    62         initial_revision.save() # saving once to set the creation date as we need it to fill the json content
    62         logger.debug("CREATE RENKAN NEW CONTENT %r", new_renkan_content)
    63         if initial_revision.modification_date != initial_revision.creation_date:
       
    64             initial_revision.modification_date = initial_revision.creation_date
       
    65         initial_revision.title = new_renkan_title if new_renkan_title else "Untitled Renkan"
       
    66         if new_renkan_content:
    63         if new_renkan_content:
    67             new_renkan_content_dict = json.loads(new_renkan.validate_json_content(new_renkan_content))
    64             new_renkan_content_dict = json.loads(new_renkan.validate_json_content(new_renkan_content))
    68             new_renkan_content_dict["created"] = str(initial_revision.creation_date)
    65             logger.debug("CREATE RENKAN NEW CONTENT AFTER VALIDATE %r", new_renkan_content_dict)
    69             new_renkan_content_dict["updated"] = str(initial_revision.modification_date)
    66             new_renkan_content_dict["created"] = str(creation_date)
       
    67             new_renkan_content_dict["updated"] = str(creation_date)
    70         else:
    68         else:
    71             new_renkan_content_dict = {
    69             new_renkan_content_dict = {
    72                 "id": str(new_renkan.renkan_guid),
    70                 "id": str(new_renkan.renkan_guid),
    73                 "title": initial_revision.title,
    71                 "title": initial_revision.title,
    74                 "description": "",
    72                 "description": "",
    75                 "created": str(initial_revision.creation_date),
    73                 "created": str(creation_date),
    76                 "updated": str(initial_revision.modification_date),
    74                 "updated": str(creation_date),
    77                 "edges": [],
    75                 "edges": [],
    78                 "nodes": [],
    76                 "nodes": [],
    79                 "users": [],
    77                 "users": [],
    80                 "space_id": new_renkan_workspace_guid,
    78                 "space_id": str(new_renkan_workspace_guid),
    81                 "views": []
    79                 "views": []
    82             }
    80             }
    83         initial_revision.content = json.dumps(new_renkan_content_dict)
    81         initial_revision.content = json.dumps(new_renkan_content_dict)
    84         initial_revision.save()
    82         initial_revision.save()
    85         return new_renkan
    83         return new_renkan
    86 
    84 
    87 class Renkan(models.Model):
    85 class Renkan(models.Model):
    88 
    86 
    89     renkan_guid = models.UUIDField(default=uuid.uuid4, editable=False, unique=True, blank=False, null=False)
    87     renkan_guid = models.UUIDField(default=uuid.uuid4, editable=False, unique=True, blank=False, null=False)
    90     workspace_guid = models.CharField(max_length=256, blank=True, null=True)
    88     workspace = models.ForeignKey('renkanmanager.Workspace', null=True, blank=True, on_delete=models.CASCADE, related_name="renkans")
    91     current_revision_guid = models.CharField(max_length=256, blank=True, null=True)
    89     current_revision = models.ForeignKey('renkanmanager.Revision', null=True, blank=True, on_delete=models.SET_NULL, related_name='+')
    92     source_revision_guid = models.CharField(max_length=256, blank=True, null=True)
    90     source_revision = models.ForeignKey('renkanmanager.Revision', null=True, blank=True, on_delete=models.SET_NULL, related_name='copies')
    93 
    91 
    94     creator = models.ForeignKey(auth_user_model, blank=True, null=True, related_name="renkan_creator")
    92     creator = models.ForeignKey(auth_user_model, blank=True, null=True, related_name="renkan_creator")
    95     creation_date = models.DateTimeField(auto_now_add=True)
    93     creation_date = models.DateTimeField(auto_now_add=True)
    96     state = models.IntegerField(default=1)
    94     state = models.IntegerField(default=1)
    97 
    95 
    98     objects = RenkanManager()
    96     objects = RenkanManager()
    99 
    97 
   100     @property
    98     @property
   101     def revision_count(self):
    99     def revision_count(self):
   102         #TODO: check related object count
   100         return self.revisions.all().count()
   103         return Revision.objects.filter(parent_renkan_guid=self.renkan_guid).count()
   101 
   104         #return Revision.objects.filter(parent_renkan__renkan_guid=self.renkan_guid).count()
   102     @property
       
   103     def workspace_guid(self):
       
   104         return self.workspace and self.workspace.workspace_guid
       
   105 
       
   106     @property
       
   107     def current_revision_guid(self):
       
   108         return self.current_revision and self.current_revision.revision_guid
       
   109 
       
   110     @property
       
   111     def source_revision_guid(self):
       
   112         return self.source_revision and self.source_revision.revision_guid
       
   113 
   105 
   114 
   106     @property
   115     @property
   107     def is_copy(self):
   116     def is_copy(self):
   108         #return bool(self.source_revision)
   117         return bool(self.source_revision)
   109         return bool(self.source_revision_guid)
       
   110 
       
   111     # Current revision object or None if there is none
       
   112 #    @property
       
   113 #    def current_revision(self):
       
   114 #        return Revision.objects.filter(parent_renkan__renkan_guid=self.renkan_guid).order_by('-creation_date').first()
       
   115 
   118 
   116     # Current revision title
   119     # Current revision title
   117     @property
   120     @property
   118     def title(self):
   121     def title(self):
   119         current_revision = Revision.objects.get(revision_guid = self.current_revision_guid)
   122         return self.current_revision and self.current_revision.title or ''
   120         return current_revision.title
       
   121         #TODO: not good -> 2 requests
       
   122         #if self.current_revision:
       
   123         #    return self.current_revision.title
       
   124         #else:
       
   125         #    return ''
       
   126 
   123 
   127     # Current revision content
   124     # Current revision content
   128     @property
   125     @property
   129     def content(self):
   126     def content(self):
   130         #TODO: not good -> 2 requests
   127         return self.current_revision and self.current_revision.content or ''
   131         current_revision = Revision.objects.get(revision_guid = self.current_revision_guid)
       
   132         return current_revision.content
       
   133         #if self.current_revision:
       
   134         #    return self.current_revision.content
       
   135         #else:
       
   136         #    return ''
       
   137 
       
   138     def __unicode__(self):
       
   139         return self.renkan_guid
       
   140 
   128 
   141     def __str__(self):
   129     def __str__(self):
   142         return self.renkan_guid
   130         return self.renkan_guid
   143 
   131 
   144     @transaction.atomic
   132     @transaction.atomic
   150         if (not timestamp) or ((self.current_revision is not None) and dateparse.parse_datetime(timestamp) < self.current_revision.modification_date):
   138         if (not timestamp) or ((self.current_revision is not None) and dateparse.parse_datetime(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)
   139             logger.error("SAVING RENKAN: provided timestamp is %r, which isn't current revision modification_date %r", timestamp, self.current_revision.modification_date)
   152             raise ValidationError(_("Cannot save, provided timestamp is invalid"))
   140             raise ValidationError(_("Cannot save, provided timestamp is invalid"))
   153         else:
   141         else:
   154             dt_timestamp = dateparse.parse_datetime(timestamp)
   142             dt_timestamp = dateparse.parse_datetime(timestamp)
   155         self.save()
   143         logger.debug("SAVE RENKAN create new revision %r", create_new_revision)
       
   144 
   156         if create_new_revision:
   145         if create_new_revision:
   157             revision_to_update = Revision(parent_renkan=self)
   146             revision_to_update = Revision(parent_renkan=self)
   158             revision_to_update.creator = updator
   147             revision_to_update.creator = updator
   159         else:
   148         else:
   160             revision_to_update = Revision.objects.select_for_update().get(revision_guid=self.current_revision.revision_guid)
   149             revision_to_update = Revision.objects.select_for_update().get(id=self.current_revision.id)
   161 
   150 
   162         updated_content = self.validate_json_content(content) if content else current_revision.content
   151         updated_content = self.validate_json_content(content) if content else current_revision.content
   163         updated_content_dict = json.loads(updated_content)
   152         updated_content_dict = json.loads(updated_content)
   164 
   153 
   165         # If title is passed as arg to the method, update the title in the json
   154         # If title is passed as arg to the method, update the title in the json
   177         revision_to_update.content = updated_content
   166         revision_to_update.content = updated_content
   178         if dt_timestamp == revision_to_update.modification_date:
   167         if dt_timestamp == revision_to_update.modification_date:
   179             revision_to_update.modification_date += datetime.resolution
   168             revision_to_update.modification_date += datetime.resolution
   180         revision_to_update.last_updated_by = updator
   169         revision_to_update.last_updated_by = updator
   181         revision_to_update.save()
   170         revision_to_update.save()
       
   171         self.save()
   182 
   172 
   183     def validate_json_content(self, content):
   173     def validate_json_content(self, content):
   184         """
   174         """
   185             Checks that the json content is valid (keys and structures), raise a ValidationError if format is wrong or value is wrong (for ids),
   175             Checks that the json content is valid (keys and structures), raise a ValidationError if format is wrong or value is wrong (for ids),
   186             if a key is missing, autocompletes with the empty default value
   176             if a key is missing, autocompletes with the empty default value
   221             raise ValidationError("Provided content has an invalid 'views' key: not a list")
   211             raise ValidationError("Provided content has an invalid 'views' key: not a list")
   222         if type(content_to_validate_dict["users"]) is not list:
   212         if type(content_to_validate_dict["users"]) is not list:
   223             raise ValidationError("Provided content has an invalid 'users' key: not a list")
   213             raise ValidationError("Provided content has an invalid 'users' key: not a list")
   224         return json.dumps(content_to_validate_dict)
   214         return json.dumps(content_to_validate_dict)
   225 
   215 
   226     #TODO:
       
   227     # @transaction.atomic
       
   228     # def delete(self):
       
   229     #     """
       
   230     #         Deleting a renkan also deletes every related revision
       
   231     #     """
       
   232     #     renkan_revisions = Revision.objects.filter(parent_renkan__renkan_guid = self.renkan_guid)
       
   233     #     for child_revision in renkan_revisions:
       
   234     #         child_revision.delete()
       
   235     #     super(Renkan, self).delete()
       
   236 
       
   237     class Meta:
   216     class Meta:
   238         app_label = 'renkanmanager'
   217         app_label = 'renkanmanager'
   239         permissions = (
   218         permissions = (
   240             ('view_renkan', 'Can view renkan'),
   219             ('view_renkan', 'Can view renkan'),
   241         )
   220         )
   242 
   221 
   243 
   222 
   244 class Revision(models.Model):
   223 class Revision(models.Model):
   245 
   224 
   246     revision_guid = models.UUIDField(default=uuid.uuid4, editable=False, unique=True, blank=False, null=False)
   225     revision_guid = models.UUIDField(default=uuid.uuid4, editable=False, unique=True, blank=False, null=False)
   247     parent_renkan_guid = models.CharField(max_length=256)
   226     parent_renkan = models.ForeignKey('renkanmanager.Renkan', on_delete=models.CASCADE, related_name="revisions")
   248     #parent_renkan = models.ForeignKey('Renkan', null=False, blank=False, to_field='renkan_guid')
       
   249     title = models.CharField(max_length=1024, null=True, blank=True)
   227     title = models.CharField(max_length=1024, null=True, blank=True)
   250     content = models.TextField(blank=True, null=True)
   228     content = models.TextField(blank=True, null=True)
   251     creator = models.ForeignKey(auth_user_model, blank=True, null=True, related_name="revision_creator")
   229     creator = models.ForeignKey(auth_user_model, blank=True, null=True, related_name="revision_creator")
   252     last_updated_by = models.ForeignKey(auth_user_model, blank=True, null=True, related_name="revision_last_updated_by")
   230     last_updated_by = models.ForeignKey(auth_user_model, blank=True, null=True, related_name="revision_last_updated_by")
   253     creation_date = models.DateTimeField(auto_now_add=True)
   231     creation_date = models.DateTimeField(auto_now_add=True, editable=False)
   254     modification_date = models.DateTimeField(auto_now=True)
   232     modification_date = models.DateTimeField()
   255     #modification_date = models.DateTimeField()
   233 
       
   234     @property
       
   235     def parent_renkan_guid(self):
       
   236         return self.parent_renkan and self.parent_renkan.renkan_guid
   256 
   237 
   257     @property
   238     @property
   258     def is_current_revision(self):
   239     def is_current_revision(self):
   259         try:
   240         return self == self.parent_renkan.current_revision
   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
       
   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
       
   265         #return self.parent_renkan.current_revision.revision_guid == self.revision_guid
       
   266 
   241 
   267     class Meta:
   242     class Meta:
   268         app_label = 'renkanmanager'
   243         app_label = 'renkanmanager'
       
   244         ordering = ['-modification_date']
   269         permissions = (
   245         permissions = (
   270             ('view_revision', 'Can view revision'),
   246             ('view_revision', 'Can view revision'),
   271         )
   247         )
       
   248 
       
   249     def __str__(self):
       
   250         return str(self.revision_guid)