Added history information for some motion views.

This commit is contained in:
Norman Jäckel 2019-01-19 15:49:46 +01:00
parent 3582e71bba
commit 9db12293a3
6 changed files with 114 additions and 32 deletions

View File

@ -17,11 +17,12 @@ Core:
- Enabled docs for using OpenSlides with Gunicorn and Uvicorn in big
mode [#3799, #3817].
- 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].
- Switch from Yarn back to npm [#3964].
- Added password reset link (password reset via email) [#3914].
- Added global history mode [#3977].
- Projector Refactor [4119, #4130].
- Added global history mode [#3977, #4141].
- Projector refactoring [4119, #4130].
Agenda:
- Added viewpoint to assign multiple items to a new parent item [#4037].

View 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={}),
),
]

View File

@ -286,7 +286,8 @@ class HistoryManager(models.Manager):
element["collection_string"], element["id"]
),
now=history_time,
information=element.get("information", ""),
information=element.get("information", []),
restricted=element.get("restricted", False),
user_id=element.get("user_id"),
full_data=data,
)
@ -334,7 +335,9 @@ class History(RESTModelMixin, models.Model):
now = models.DateTimeField()
information = models.CharField(max_length=255)
information = JSONField()
restricted = models.BooleanField(default=False)
user = models.ForeignKey(
settings.AUTH_USER_MODEL, null=True, on_delete=SET_NULL_AND_AUTOUPDATE

View File

@ -169,5 +169,5 @@ class HistorySerializer(ModelSerializer):
class Meta:
model = History
fields = ("id", "element_id", "now", "information", "user")
fields = ("id", "element_id", "now", "information", "restricted", "user")
read_only_fields = ("now",)

View File

@ -124,7 +124,7 @@ class MotionViewSet(ModelViewSet):
# Fire autoupdate again to save information to OpenSlides history.
inform_deleted_data(
[(motion.get_collection_string(), motion.pk)],
information="Motion deleted",
information=["Motion deleted"],
user_id=request.user.pk,
)
@ -221,10 +221,16 @@ class MotionViewSet(ModelViewSet):
# Send new submitters and supporters via autoupdate because users
# 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.extend(motion.supporters.all())
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)
# Strip out response data so nobody gets unrestricted data.
data = ReturnDict(id=serializer.data.get("id"), serializer=serializer)
@ -298,12 +304,13 @@ class MotionViewSet(ModelViewSet):
# Send new supporters via autoupdate because users
# without permission to see users may not have them but can get it now.
# TODO: Skip history.
new_users = list(updated_motion.supporters.all())
inform_changed_data(new_users)
# Fire autoupdate again to save information to OpenSlides history.
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.
@ -396,7 +403,7 @@ class MotionViewSet(ModelViewSet):
# write log
motion.write_log([f"Comment {section.name} updated"], request.user)
message = f"Comment {section.name} updated"
message = ["Comment {arg1} updated", section.name]
else: # DELETE
try:
comment = MotionComment.objects.get(motion=motion, section=section)
@ -407,7 +414,12 @@ class MotionViewSet(ModelViewSet):
comment.delete()
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})
@ -475,7 +487,7 @@ class MotionViewSet(ModelViewSet):
motion_result.append(motion)
# 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
# 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)
# Send new supporter via autoupdate because users without permission
# to see users may not have it but can get it now.
# TODO: Skip history.
inform_changed_data([request.user])
message = "You have supported this motion successfully."
else:
@ -523,6 +536,11 @@ class MotionViewSet(ModelViewSet):
motion.write_log(["Motion unsupported"], request.user)
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.
return Response({"detail": message})
@ -569,11 +587,10 @@ class MotionViewSet(ModelViewSet):
person=request.user,
skip_autoupdate=True,
)
inform_changed_data(
motion,
information=f"State set to {motion.state.name}.",
user_id=request.user.pk,
)
# 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)
return Response({"detail": message})
@list_route(methods=["post"])
@ -640,15 +657,17 @@ class MotionViewSet(ModelViewSet):
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.
motion_result.append(motion)
# Now inform all clients.
inform_changed_data(motion_result)
# Send 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"])
@ -703,7 +722,12 @@ class MotionViewSet(ModelViewSet):
person=request.user,
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})
@list_route(methods=["post"])
@ -784,12 +808,14 @@ class MotionViewSet(ModelViewSet):
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.
motion_result.append(motion)
# Now inform all clients.
inform_changed_data(motion_result)
# Send response.
return Response(
{"detail": f"{len(motion_result)} motions successfully updated."}
@ -826,7 +852,8 @@ class MotionViewSet(ModelViewSet):
)
# 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."})
@detail_route(methods=["post"])
@ -846,7 +873,11 @@ class MotionViewSet(ModelViewSet):
raise ValidationError({"detail": err})
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(
{"detail": "Vote created successfully.", "createdPollId": poll.pk}
)
@ -939,6 +970,12 @@ class MotionPollViewSet(UpdateModelMixin, DestroyModelMixin, GenericViewSet):
response = super().update(*args, **kwargs)
poll = self.get_object()
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
def destroy(self, *args, **kwargs):
@ -948,6 +985,12 @@ class MotionPollViewSet(UpdateModelMixin, DestroyModelMixin, GenericViewSet):
poll = self.get_object()
result = super().destroy(*args, **kwargs)
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
@ -1205,7 +1248,7 @@ class CategoryViewSet(ModelViewSet):
error_message = "Error: At least one identifier of this category does already exist in another category."
response = Response({"detail": error_message}, status=400)
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."
response = Response({"detail": message})
return response
@ -1251,7 +1294,6 @@ class MotionBlockViewSet(ModelViewSet):
its recommendation. It is a POST request without any data.
"""
motion_block = self.get_object()
instances = []
with transaction.atomic():
for motion in motion_block.motion_set.all():
# Follow recommendation.
@ -1263,8 +1305,10 @@ class MotionBlockViewSet(ModelViewSet):
person=request.user,
skip_autoupdate=True,
)
instances.append(motion)
inform_changed_data(instances)
# Fire autoupdate and save information to OpenSlides history.
inform_changed_data(
motion, information=["State set to {arg1}", motion.state.name], user_id=request.user.pk
)
return Response({"detail": "Followed recommendations successfully."})

View File

@ -31,7 +31,8 @@ class Element(ElementBase, total=False):
process.
"""
information: str
information: List[str]
restricted: bool
user_id: Optional[int]
disable_history: bool
reload: bool
@ -51,8 +52,9 @@ AutoupdateFormat = TypedDict(
def inform_changed_data(
instances: Union[Iterable[Model], Model],
information: str = "",
information: List[str] = None,
user_id: Optional[int] = None,
restricted: bool = False,
) -> None:
"""
Informs the autoupdate system and the caching system about the creation or
@ -62,6 +64,8 @@ def inform_changed_data(
History creation is enabled.
"""
if information is None:
information = []
root_instances = set()
if not isinstance(instances, Iterable):
instances = (instances,)
@ -81,6 +85,7 @@ def inform_changed_data(
collection_string=root_instance.get_collection_string(),
full_data=root_instance.get_full_data(),
information=information,
restricted=restricted,
user_id=user_id,
)
@ -95,8 +100,9 @@ def inform_changed_data(
def inform_deleted_data(
deleted_elements: Iterable[Tuple[str, int]],
information: str = "",
information: List[str] = None,
user_id: Optional[int] = None,
restricted: bool = False,
) -> None:
"""
Informs the autoupdate system and the caching system about the deletion of
@ -104,6 +110,8 @@ def inform_deleted_data(
History creation is enabled.
"""
if information is None:
information = []
elements: Dict[str, Element] = {}
for deleted_element in deleted_elements:
key = deleted_element[0] + str(deleted_element[1])
@ -112,6 +120,7 @@ def inform_deleted_data(
collection_string=deleted_element[0],
full_data=None,
information=information,
restricted=restricted,
user_id=user_id,
)