Merge pull request #4141 from normanjaeckel/HistoryInformation
Added history information for some motion views.
This commit is contained in:
commit
a83de77180
@ -17,11 +17,12 @@ Core:
|
|||||||
- Enabled docs for using OpenSlides with Gunicorn and Uvicorn in big
|
- Enabled docs for using OpenSlides with Gunicorn and Uvicorn in big
|
||||||
mode [#3799, #3817].
|
mode [#3799, #3817].
|
||||||
- Changed format for elements send via autoupdate [#3926].
|
- Changed format for elements send via autoupdate [#3926].
|
||||||
|
- Fixed autoupdate system for related objects [#4140].
|
||||||
- Add a change-id system to get only new elements [#3938].
|
- Add a change-id system to get only new elements [#3938].
|
||||||
- Switch from Yarn back to npm [#3964].
|
- Switch from Yarn back to npm [#3964].
|
||||||
- Added password reset link (password reset via email) [#3914].
|
- Added password reset link (password reset via email) [#3914].
|
||||||
- Added global history mode [#3977].
|
- Added global history mode [#3977, #4141].
|
||||||
- Projector Refactor [4119, #4130].
|
- Projector refactoring [4119, #4130].
|
||||||
|
|
||||||
Agenda:
|
Agenda:
|
||||||
- Added viewpoint to assign multiple items to a new parent item [#4037].
|
- Added viewpoint to assign multiple items to a new parent item [#4037].
|
||||||
|
25
openslides/core/migrations/0013_auto_20190119_1641.py
Normal file
25
openslides/core/migrations/0013_auto_20190119_1641.py
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
# Generated by Django 2.1.5 on 2019-01-19 15:41
|
||||||
|
|
||||||
|
import jsonfield.encoder
|
||||||
|
import jsonfield.fields
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('core', '0012_auto_20190119_1425'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='history',
|
||||||
|
name='restricted',
|
||||||
|
field=models.BooleanField(default=False),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='history',
|
||||||
|
name='information',
|
||||||
|
field=jsonfield.fields.JSONField(dump_kwargs={'cls': jsonfield.encoder.JSONEncoder, 'separators': (',', ':')}, load_kwargs={}),
|
||||||
|
),
|
||||||
|
]
|
@ -286,7 +286,8 @@ class HistoryManager(models.Manager):
|
|||||||
element["collection_string"], element["id"]
|
element["collection_string"], element["id"]
|
||||||
),
|
),
|
||||||
now=history_time,
|
now=history_time,
|
||||||
information=element.get("information", ""),
|
information=element.get("information", []),
|
||||||
|
restricted=element.get("restricted", False),
|
||||||
user_id=element.get("user_id"),
|
user_id=element.get("user_id"),
|
||||||
full_data=data,
|
full_data=data,
|
||||||
)
|
)
|
||||||
@ -334,7 +335,9 @@ class History(RESTModelMixin, models.Model):
|
|||||||
|
|
||||||
now = models.DateTimeField()
|
now = models.DateTimeField()
|
||||||
|
|
||||||
information = models.CharField(max_length=255)
|
information = JSONField()
|
||||||
|
|
||||||
|
restricted = models.BooleanField(default=False)
|
||||||
|
|
||||||
user = models.ForeignKey(
|
user = models.ForeignKey(
|
||||||
settings.AUTH_USER_MODEL, null=True, on_delete=SET_NULL_AND_AUTOUPDATE
|
settings.AUTH_USER_MODEL, null=True, on_delete=SET_NULL_AND_AUTOUPDATE
|
||||||
|
@ -169,5 +169,5 @@ class HistorySerializer(ModelSerializer):
|
|||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = History
|
model = History
|
||||||
fields = ("id", "element_id", "now", "information", "user")
|
fields = ("id", "element_id", "now", "information", "restricted", "user")
|
||||||
read_only_fields = ("now",)
|
read_only_fields = ("now",)
|
||||||
|
@ -124,7 +124,7 @@ class MotionViewSet(ModelViewSet):
|
|||||||
# Fire autoupdate again to save information to OpenSlides history.
|
# Fire autoupdate again to save information to OpenSlides history.
|
||||||
inform_deleted_data(
|
inform_deleted_data(
|
||||||
[(motion.get_collection_string(), motion.pk)],
|
[(motion.get_collection_string(), motion.pk)],
|
||||||
information="Motion deleted",
|
information=["Motion deleted"],
|
||||||
user_id=request.user.pk,
|
user_id=request.user.pk,
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -221,10 +221,16 @@ class MotionViewSet(ModelViewSet):
|
|||||||
|
|
||||||
# Send new submitters and supporters via autoupdate because users
|
# Send new submitters and supporters via autoupdate because users
|
||||||
# without permission to see users may not have them but can get it now.
|
# without permission to see users may not have them but can get it now.
|
||||||
|
# TODO: Skip history.
|
||||||
new_users = list(motion.submitters.all())
|
new_users = list(motion.submitters.all())
|
||||||
new_users.extend(motion.supporters.all())
|
new_users.extend(motion.supporters.all())
|
||||||
inform_changed_data(new_users)
|
inform_changed_data(new_users)
|
||||||
|
|
||||||
|
# Fire autoupdate again to save information to OpenSlides history.
|
||||||
|
inform_changed_data(
|
||||||
|
motion, information=["Motion created"], user_id=request.user.pk,
|
||||||
|
)
|
||||||
|
|
||||||
headers = self.get_success_headers(serializer.data)
|
headers = self.get_success_headers(serializer.data)
|
||||||
# Strip out response data so nobody gets unrestricted data.
|
# Strip out response data so nobody gets unrestricted data.
|
||||||
data = ReturnDict(id=serializer.data.get("id"), serializer=serializer)
|
data = ReturnDict(id=serializer.data.get("id"), serializer=serializer)
|
||||||
@ -298,12 +304,13 @@ class MotionViewSet(ModelViewSet):
|
|||||||
|
|
||||||
# Send new supporters via autoupdate because users
|
# Send new supporters via autoupdate because users
|
||||||
# without permission to see users may not have them but can get it now.
|
# without permission to see users may not have them but can get it now.
|
||||||
|
# TODO: Skip history.
|
||||||
new_users = list(updated_motion.supporters.all())
|
new_users = list(updated_motion.supporters.all())
|
||||||
inform_changed_data(new_users)
|
inform_changed_data(new_users)
|
||||||
|
|
||||||
# Fire autoupdate again to save information to OpenSlides history.
|
# Fire autoupdate again to save information to OpenSlides history.
|
||||||
inform_changed_data(
|
inform_changed_data(
|
||||||
updated_motion, information="Motion updated", user_id=request.user.pk
|
updated_motion, information=["Motion updated"], user_id=request.user.pk
|
||||||
)
|
)
|
||||||
|
|
||||||
# We do not add serializer.data to response so nobody gets unrestricted data here.
|
# We do not add serializer.data to response so nobody gets unrestricted data here.
|
||||||
@ -396,7 +403,7 @@ class MotionViewSet(ModelViewSet):
|
|||||||
|
|
||||||
# write log
|
# write log
|
||||||
motion.write_log([f"Comment {section.name} updated"], request.user)
|
motion.write_log([f"Comment {section.name} updated"], request.user)
|
||||||
message = f"Comment {section.name} updated"
|
message = ["Comment {arg1} updated", section.name]
|
||||||
else: # DELETE
|
else: # DELETE
|
||||||
try:
|
try:
|
||||||
comment = MotionComment.objects.get(motion=motion, section=section)
|
comment = MotionComment.objects.get(motion=motion, section=section)
|
||||||
@ -407,7 +414,12 @@ class MotionViewSet(ModelViewSet):
|
|||||||
comment.delete()
|
comment.delete()
|
||||||
|
|
||||||
motion.write_log([f"Comment {section.name} deleted"], request.user)
|
motion.write_log([f"Comment {section.name} deleted"], request.user)
|
||||||
message = f"Comment {section.name} deleted"
|
message = ["Comment {arg1} deleted", section.name]
|
||||||
|
|
||||||
|
# Fire autoupdate again to save information to OpenSlides history.
|
||||||
|
inform_changed_data(
|
||||||
|
motion, information=message, user_id=request.user.pk, restricted=True,
|
||||||
|
)
|
||||||
|
|
||||||
return Response({"detail": message})
|
return Response({"detail": message})
|
||||||
|
|
||||||
@ -475,7 +487,7 @@ class MotionViewSet(ModelViewSet):
|
|||||||
motion_result.append(motion)
|
motion_result.append(motion)
|
||||||
|
|
||||||
# Now inform all clients.
|
# Now inform all clients.
|
||||||
inform_changed_data(motion_result)
|
inform_changed_data(motion_result, information=["Submitters changed"], user_id=request.user.pk)
|
||||||
|
|
||||||
# Also send all new submitters via autoupdate because users without
|
# Also send all new submitters via autoupdate because users without
|
||||||
# permission to see users may not have them but can get it now.
|
# permission to see users may not have them but can get it now.
|
||||||
@ -512,6 +524,7 @@ class MotionViewSet(ModelViewSet):
|
|||||||
motion.write_log(["Motion supported"], request.user)
|
motion.write_log(["Motion supported"], request.user)
|
||||||
# Send new supporter via autoupdate because users without permission
|
# Send new supporter via autoupdate because users without permission
|
||||||
# to see users may not have it but can get it now.
|
# to see users may not have it but can get it now.
|
||||||
|
# TODO: Skip history.
|
||||||
inform_changed_data([request.user])
|
inform_changed_data([request.user])
|
||||||
message = "You have supported this motion successfully."
|
message = "You have supported this motion successfully."
|
||||||
else:
|
else:
|
||||||
@ -523,6 +536,11 @@ class MotionViewSet(ModelViewSet):
|
|||||||
motion.write_log(["Motion unsupported"], request.user)
|
motion.write_log(["Motion unsupported"], request.user)
|
||||||
message = "You have unsupported this motion successfully."
|
message = "You have unsupported this motion successfully."
|
||||||
|
|
||||||
|
# Fire autoupdate again to save information to OpenSlides history.
|
||||||
|
inform_changed_data(
|
||||||
|
motion, information=["Supporters changed"], user_id=request.user.pk
|
||||||
|
)
|
||||||
|
|
||||||
# Initiate response.
|
# Initiate response.
|
||||||
return Response({"detail": message})
|
return Response({"detail": message})
|
||||||
|
|
||||||
@ -569,11 +587,10 @@ class MotionViewSet(ModelViewSet):
|
|||||||
person=request.user,
|
person=request.user,
|
||||||
skip_autoupdate=True,
|
skip_autoupdate=True,
|
||||||
)
|
)
|
||||||
inform_changed_data(
|
|
||||||
motion,
|
# Fire autoupdate again to save information to OpenSlides history.
|
||||||
information=f"State set to {motion.state.name}.",
|
inform_changed_data(motion, information=["State set to {arg1}", motion.state.name], user_id=request.user.pk)
|
||||||
user_id=request.user.pk,
|
|
||||||
)
|
|
||||||
return Response({"detail": message})
|
return Response({"detail": message})
|
||||||
|
|
||||||
@list_route(methods=["post"])
|
@list_route(methods=["post"])
|
||||||
@ -640,15 +657,17 @@ class MotionViewSet(ModelViewSet):
|
|||||||
skip_autoupdate=True,
|
skip_autoupdate=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Fire autoupdate again to save information to OpenSlides history.
|
||||||
|
inform_changed_data(
|
||||||
|
motion, information=["State set to {arg1}", motion.state.name], user_id=request.user.pk,
|
||||||
|
)
|
||||||
|
|
||||||
# Finish motion.
|
# Finish motion.
|
||||||
motion_result.append(motion)
|
motion_result.append(motion)
|
||||||
|
|
||||||
# Now inform all clients.
|
|
||||||
inform_changed_data(motion_result)
|
|
||||||
|
|
||||||
# Send response.
|
# Send response.
|
||||||
return Response(
|
return Response(
|
||||||
{"detail": f"{len(motion_result)} motions successfully updated."}
|
{"detail": f"State of {len(motion_result)} motions successfully set."}
|
||||||
)
|
)
|
||||||
|
|
||||||
@detail_route(methods=["put"])
|
@detail_route(methods=["put"])
|
||||||
@ -703,7 +722,12 @@ class MotionViewSet(ModelViewSet):
|
|||||||
person=request.user,
|
person=request.user,
|
||||||
skip_autoupdate=True,
|
skip_autoupdate=True,
|
||||||
)
|
)
|
||||||
inform_changed_data(motion)
|
|
||||||
|
# Fire autoupdate again to save information to OpenSlides history.
|
||||||
|
inform_changed_data(
|
||||||
|
motion, information=["Recommendation set to {arg1}", label], user_id=request.user.pk,
|
||||||
|
)
|
||||||
|
|
||||||
return Response({"detail": message})
|
return Response({"detail": message})
|
||||||
|
|
||||||
@list_route(methods=["post"])
|
@list_route(methods=["post"])
|
||||||
@ -784,12 +808,14 @@ class MotionViewSet(ModelViewSet):
|
|||||||
skip_autoupdate=True,
|
skip_autoupdate=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Fire autoupdate and save information to OpenSlides history.
|
||||||
|
inform_changed_data(
|
||||||
|
motion, information=["Recommendation set to {arg1}", label], user_id=request.user.pk,
|
||||||
|
)
|
||||||
|
|
||||||
# Finish motion.
|
# Finish motion.
|
||||||
motion_result.append(motion)
|
motion_result.append(motion)
|
||||||
|
|
||||||
# Now inform all clients.
|
|
||||||
inform_changed_data(motion_result)
|
|
||||||
|
|
||||||
# Send response.
|
# Send response.
|
||||||
return Response(
|
return Response(
|
||||||
{"detail": f"{len(motion_result)} motions successfully updated."}
|
{"detail": f"{len(motion_result)} motions successfully updated."}
|
||||||
@ -826,7 +852,8 @@ class MotionViewSet(ModelViewSet):
|
|||||||
)
|
)
|
||||||
|
|
||||||
# Now send all changes to the clients.
|
# Now send all changes to the clients.
|
||||||
inform_changed_data(motion)
|
inform_changed_data(motion, information=["State set to {arg1}", motion.state.name], user_id=request.user.pk)
|
||||||
|
|
||||||
return Response({"detail": "Recommendation followed successfully."})
|
return Response({"detail": "Recommendation followed successfully."})
|
||||||
|
|
||||||
@detail_route(methods=["post"])
|
@detail_route(methods=["post"])
|
||||||
@ -846,7 +873,11 @@ class MotionViewSet(ModelViewSet):
|
|||||||
raise ValidationError({"detail": err})
|
raise ValidationError({"detail": err})
|
||||||
motion.write_log(["Vote created"], request.user, skip_autoupdate=True)
|
motion.write_log(["Vote created"], request.user, skip_autoupdate=True)
|
||||||
|
|
||||||
inform_changed_data(motion)
|
# Fire autoupdate again to save information to OpenSlides history.
|
||||||
|
inform_changed_data(
|
||||||
|
motion, information=["Vote created"], user_id=request.user.pk,
|
||||||
|
)
|
||||||
|
|
||||||
return Response(
|
return Response(
|
||||||
{"detail": "Vote created successfully.", "createdPollId": poll.pk}
|
{"detail": "Vote created successfully.", "createdPollId": poll.pk}
|
||||||
)
|
)
|
||||||
@ -939,6 +970,12 @@ class MotionPollViewSet(UpdateModelMixin, DestroyModelMixin, GenericViewSet):
|
|||||||
response = super().update(*args, **kwargs)
|
response = super().update(*args, **kwargs)
|
||||||
poll = self.get_object()
|
poll = self.get_object()
|
||||||
poll.motion.write_log(["Vote updated"], self.request.user)
|
poll.motion.write_log(["Vote updated"], self.request.user)
|
||||||
|
|
||||||
|
# Fire autoupdate again to save information to OpenSlides history.
|
||||||
|
inform_changed_data(
|
||||||
|
poll.motion, information=["Poll updated"], user_id=self.request.user.pk
|
||||||
|
)
|
||||||
|
|
||||||
return response
|
return response
|
||||||
|
|
||||||
def destroy(self, *args, **kwargs):
|
def destroy(self, *args, **kwargs):
|
||||||
@ -948,6 +985,12 @@ class MotionPollViewSet(UpdateModelMixin, DestroyModelMixin, GenericViewSet):
|
|||||||
poll = self.get_object()
|
poll = self.get_object()
|
||||||
result = super().destroy(*args, **kwargs)
|
result = super().destroy(*args, **kwargs)
|
||||||
poll.motion.write_log(["Vote deleted"], self.request.user)
|
poll.motion.write_log(["Vote deleted"], self.request.user)
|
||||||
|
|
||||||
|
# Fire autoupdate again to save information to OpenSlides history.
|
||||||
|
inform_changed_data(
|
||||||
|
poll.motion, information=["Poll deleted"], user_id=self.request.user.pk
|
||||||
|
)
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
@ -1205,7 +1248,7 @@ class CategoryViewSet(ModelViewSet):
|
|||||||
error_message = "Error: At least one identifier of this category does already exist in another category."
|
error_message = "Error: At least one identifier of this category does already exist in another category."
|
||||||
response = Response({"detail": error_message}, status=400)
|
response = Response({"detail": error_message}, status=400)
|
||||||
else:
|
else:
|
||||||
inform_changed_data(instances)
|
inform_changed_data(instances, information=["Number set"], user_id=request.user.pk)
|
||||||
message = f"All motions in category {category} numbered " "successfully."
|
message = f"All motions in category {category} numbered " "successfully."
|
||||||
response = Response({"detail": message})
|
response = Response({"detail": message})
|
||||||
return response
|
return response
|
||||||
@ -1251,7 +1294,6 @@ class MotionBlockViewSet(ModelViewSet):
|
|||||||
its recommendation. It is a POST request without any data.
|
its recommendation. It is a POST request without any data.
|
||||||
"""
|
"""
|
||||||
motion_block = self.get_object()
|
motion_block = self.get_object()
|
||||||
instances = []
|
|
||||||
with transaction.atomic():
|
with transaction.atomic():
|
||||||
for motion in motion_block.motion_set.all():
|
for motion in motion_block.motion_set.all():
|
||||||
# Follow recommendation.
|
# Follow recommendation.
|
||||||
@ -1263,8 +1305,10 @@ class MotionBlockViewSet(ModelViewSet):
|
|||||||
person=request.user,
|
person=request.user,
|
||||||
skip_autoupdate=True,
|
skip_autoupdate=True,
|
||||||
)
|
)
|
||||||
instances.append(motion)
|
# Fire autoupdate and save information to OpenSlides history.
|
||||||
inform_changed_data(instances)
|
inform_changed_data(
|
||||||
|
motion, information=["State set to {arg1}", motion.state.name], user_id=request.user.pk
|
||||||
|
)
|
||||||
return Response({"detail": "Followed recommendations successfully."})
|
return Response({"detail": "Followed recommendations successfully."})
|
||||||
|
|
||||||
|
|
||||||
|
@ -31,7 +31,8 @@ class Element(ElementBase, total=False):
|
|||||||
process.
|
process.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
information: str
|
information: List[str]
|
||||||
|
restricted: bool
|
||||||
user_id: Optional[int]
|
user_id: Optional[int]
|
||||||
disable_history: bool
|
disable_history: bool
|
||||||
reload: bool
|
reload: bool
|
||||||
@ -51,8 +52,9 @@ AutoupdateFormat = TypedDict(
|
|||||||
|
|
||||||
def inform_changed_data(
|
def inform_changed_data(
|
||||||
instances: Union[Iterable[Model], Model],
|
instances: Union[Iterable[Model], Model],
|
||||||
information: str = "",
|
information: List[str] = None,
|
||||||
user_id: Optional[int] = None,
|
user_id: Optional[int] = None,
|
||||||
|
restricted: bool = False,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""
|
"""
|
||||||
Informs the autoupdate system and the caching system about the creation or
|
Informs the autoupdate system and the caching system about the creation or
|
||||||
@ -62,6 +64,8 @@ def inform_changed_data(
|
|||||||
|
|
||||||
History creation is enabled.
|
History creation is enabled.
|
||||||
"""
|
"""
|
||||||
|
if information is None:
|
||||||
|
information = []
|
||||||
root_instances = set()
|
root_instances = set()
|
||||||
if not isinstance(instances, Iterable):
|
if not isinstance(instances, Iterable):
|
||||||
instances = (instances,)
|
instances = (instances,)
|
||||||
@ -81,6 +85,7 @@ def inform_changed_data(
|
|||||||
collection_string=root_instance.get_collection_string(),
|
collection_string=root_instance.get_collection_string(),
|
||||||
full_data=root_instance.get_full_data(),
|
full_data=root_instance.get_full_data(),
|
||||||
information=information,
|
information=information,
|
||||||
|
restricted=restricted,
|
||||||
user_id=user_id,
|
user_id=user_id,
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -95,8 +100,9 @@ def inform_changed_data(
|
|||||||
|
|
||||||
def inform_deleted_data(
|
def inform_deleted_data(
|
||||||
deleted_elements: Iterable[Tuple[str, int]],
|
deleted_elements: Iterable[Tuple[str, int]],
|
||||||
information: str = "",
|
information: List[str] = None,
|
||||||
user_id: Optional[int] = None,
|
user_id: Optional[int] = None,
|
||||||
|
restricted: bool = False,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""
|
"""
|
||||||
Informs the autoupdate system and the caching system about the deletion of
|
Informs the autoupdate system and the caching system about the deletion of
|
||||||
@ -104,6 +110,8 @@ def inform_deleted_data(
|
|||||||
|
|
||||||
History creation is enabled.
|
History creation is enabled.
|
||||||
"""
|
"""
|
||||||
|
if information is None:
|
||||||
|
information = []
|
||||||
elements: Dict[str, Element] = {}
|
elements: Dict[str, Element] = {}
|
||||||
for deleted_element in deleted_elements:
|
for deleted_element in deleted_elements:
|
||||||
key = deleted_element[0] + str(deleted_element[1])
|
key = deleted_element[0] + str(deleted_element[1])
|
||||||
@ -112,6 +120,7 @@ def inform_deleted_data(
|
|||||||
collection_string=deleted_element[0],
|
collection_string=deleted_element[0],
|
||||||
full_data=None,
|
full_data=None,
|
||||||
information=information,
|
information=information,
|
||||||
|
restricted=restricted,
|
||||||
user_id=user_id,
|
user_id=user_id,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user