diff --git a/biz/admin.py b/biz/admin.py
index e1a4556..a83003e 100644
--- a/biz/admin.py
+++ b/biz/admin.py
@@ -429,57 +429,3 @@ class ActivityAdmin(SyncedObjectAdmin):
def get_event_display(self, obj):
return str(obj)
get_event_display.short_description = 'Activity'
-
-# @admin.register(EmailCampaign)
-# class EmailCampaignAdmin(admin.ModelAdmin):
-# list_display = ('subject', 'event', 'sent_at')
-# list_filter = ('sent_at',)
-# search_fields = ('subject', 'content')
-# date_hierarchy = 'sent_at'
-# readonly_fields = ('sent_at',)
-
-# @admin.register(EmailTracker)
-# class EmailTrackerAdmin(admin.ModelAdmin):
-# list_display = (
-# 'campaign',
-# 'prospect',
-# 'tracking_id',
-# 'sent_status',
-# 'opened_status',
-# 'clicked_status'
-# )
-# list_filter = ('sent', 'opened', 'clicked')
-# search_fields = (
-# 'prospect__name',
-# 'prospect__email',
-# 'campaign__subject'
-# )
-# readonly_fields = (
-# 'tracking_id', 'sent', 'sent_at',
-# 'opened', 'opened_at',
-# 'clicked', 'clicked_at'
-# )
-# date_hierarchy = 'sent_at'
-
-# def sent_status(self, obj):
-# return self._get_status_html(obj.sent, obj.sent_at)
-# sent_status.short_description = 'Sent'
-# sent_status.allow_tags = True
-
-# def opened_status(self, obj):
-# return self._get_status_html(obj.opened, obj.opened_at)
-# opened_status.short_description = 'Opened'
-# opened_status.allow_tags = True
-
-# def clicked_status(self, obj):
-# return self._get_status_html(obj.clicked, obj.clicked_at)
-# clicked_status.short_description = 'Clicked'
-# clicked_status.allow_tags = True
-
-# def _get_status_html(self, status, date):
-# if status:
-# return format_html(
-# '✓ {}',
-# date.strftime('%Y-%m-%d %H:%M') if date else ''
-# )
-# return format_html('✗')
diff --git a/sync/README.md b/sync/README.md
new file mode 100644
index 0000000..b309631
--- /dev/null
+++ b/sync/README.md
@@ -0,0 +1,21 @@
+### Synchronization quick ReadMe
+
+- Data class must extend BaseModel
+- Admin classes must extend SyncedObjectAdmin to have updates saved in the BaseModel properties
+- The SynchronizationApi defines a get and a post service to POST new data, and GET updates. When performing an operation on a data, a ModelLog instance is created with the related information. When performing a GET, we retrieve the list of ModelLogs to sent the new data to the user.
+- routing.py defines the URL of the websocket where messages are sent when updates are made. URL is by user.
+
+
+### Sharing
+
+- Data can be shared between users. To do that, a new DataAccess object can be created to define the owner, the authorized user, and the object id.
+- By default, the whole hierarchy of objects are shared, from the data parents to all its children.
+- Special data path can be specified for a class by defining a setting
+
+example:
+SYNC_MODEL_CHILDREN_SHARING = {
+ 'Match': ['team_scores', 'team_registration', 'player_registrations']
+}
+Here when sharing a Match, we also share objects accessed through the names of the properties to get TeamScore, TeamRegistration and PlayerRegistration.
+
+- It's also possible to exclude a class from being sharable by setting sharable = False in its definition. In PadelClub, Club is the top entity that links all data together, so we don't want the automatic data scanning to share clubs.
diff --git a/sync/admin.py b/sync/admin.py
index 0f9f713..296df11 100644
--- a/sync/admin.py
+++ b/sync/admin.py
@@ -5,7 +5,7 @@ from .models import BaseModel, ModelLog, DataAccess
class SyncedObjectAdmin(admin.ModelAdmin):
- exclude = ('data_access_ids',)
+ # exclude = ('data_access_ids',)
raw_id_fields = ['related_user', 'last_updated_by']
def save_model(self, request, obj, form, change):
diff --git a/sync/models/base.py b/sync/models/base.py
index 562dd56..564def2 100644
--- a/sync/models/base.py
+++ b/sync/models/base.py
@@ -16,6 +16,8 @@ class BaseModel(models.Model):
last_updated_by = models.ForeignKey(settings.AUTH_USER_MODEL, blank=True, null=True, on_delete=models.SET_NULL, related_name='+')
data_access_ids = models.JSONField(default=list)
+ sharable = True
+
class Meta:
abstract = True
@@ -39,6 +41,8 @@ class BaseModel(models.Model):
}
def update_data_access_list(self):
+ if self.sharable == False:
+ return
related_instances = self.sharing_related_instances()
data_access_ids = set()
for instance in related_instances:
@@ -46,21 +50,18 @@ class BaseModel(models.Model):
data_access_ids.update(instance.data_access_ids)
# print(f'related_instances = {related_instances}')
- # data_access_ids = [instance.data_access_ids for instance in related_instances if isinstance(instance, BaseModel)]
- # data_access_ids.extend(self.data_access_ids)
self.data_access_ids = list(data_access_ids)
- # DataAccess = apps.get_model('sync', 'DataAccess')
- # data_accesses = DataAccess.objects.filter(model_id__in=related_ids)
- # for data_access in data_accesses:
- # self.add_data_access_relation(data_access)
-
def add_data_access_relation(self, data_access):
+ if self.sharable == False:
+ return
str_id = str(data_access.id)
if str_id not in self.data_access_ids:
self.data_access_ids.append(str_id)
def remove_data_access_relation(self, data_access):
+ if self.sharable == False:
+ return
try:
self.data_access_ids.remove(str(data_access.id))
except ValueError:
diff --git a/tournaments/admin.py b/tournaments/admin.py
index 4febf0d..b8fd869 100644
--- a/tournaments/admin.py
+++ b/tournaments/admin.py
@@ -95,7 +95,7 @@ class EventAdmin(SyncedObjectAdmin):
readonly_fields = ['display_images_preview']
fieldsets = [
- (None, {'fields': ['last_update', 'related_user', 'name', 'club', 'creator', 'creation_date', 'tenup_id']}),
+ (None, {'fields': ['data_access_ids','last_update', 'related_user', 'name', 'club', 'creator', 'creation_date', 'tenup_id']}),
('Images', {'fields': ['display_images_preview'], 'classes': ['collapse']}),
]
diff --git a/tournaments/models/club.py b/tournaments/models/club.py
index d0f6b56..3582f51 100644
--- a/tournaments/models/club.py
+++ b/tournaments/models/club.py
@@ -27,6 +27,8 @@ class Club(BaseModel):
broadcast_code = models.CharField(max_length=10, null=True, blank=True, unique=True)
admin_visible = models.BooleanField(default=False)
+ sharable = False
+
def delete_dependencies(self):
for court in self.courts.all():
# court.delete_dependencies()