-
Notifications
You must be signed in to change notification settings - Fork 26
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Deleted participants stay present in meeting #2730
Changes from 2 commits
7b5f725
3f0a7ec
c389c81
9536209
559fd18
eef214f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,5 @@ | ||
{ | ||
"_migration_index": 62, | ||
"_migration_index": 63, | ||
"gender":{ | ||
"1":{ | ||
"id": 1, | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,5 @@ | ||
{ | ||
"_migration_index": 62, | ||
"_migration_index": 63, | ||
"gender":{ | ||
"1":{ | ||
"id": 1, | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -17,6 +17,7 @@ | |
from ...mixins.archived_meeting_check_mixin import CheckForArchivedMeetingMixin | ||
from ...util.typing import ActionData | ||
from ..meeting_user.set_data import MeetingUserSetData | ||
from .set_present import UserSetPresentAction | ||
|
||
|
||
class UsernameMixin(Action): | ||
|
@@ -142,10 +143,19 @@ def strip_field(self, field: str, instance: dict[str, Any]) -> None: | |
def check_meeting_and_users( | ||
self, instance: dict[str, Any], user_fqid: FullQualifiedId | ||
) -> None: | ||
if instance.get("meeting_id") is not None: | ||
self.datastore.apply_changed_model( | ||
user_fqid, {"meeting_id": instance.get("meeting_id")} | ||
) | ||
if (meeting_id := instance.get("meeting_id")) is not None: | ||
if instance.get("group_ids") == []: | ||
self.execute_other_action( | ||
UserSetPresentAction, | ||
[ | ||
{ | ||
"id": instance["id"], | ||
"meeting_id": meeting_id, | ||
"present": False, | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does this handle the case, if a user is deleted and presence in two meetings? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Now in its new place it does. :) |
||
], | ||
) | ||
self.datastore.apply_changed_model(user_fqid, {"meeting_id": meeting_id}) | ||
|
||
def meeting_user_set_data(self, instance: dict[str, Any]) -> None: | ||
meeting_user_data = {} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
from collections import defaultdict | ||
|
||
from datastore.migrations import BaseModelMigration | ||
from datastore.writer.core import BaseRequestEvent, RequestUpdateEvent | ||
|
||
from openslides_backend.shared.patterns import fqid_from_collection_and_id | ||
|
||
|
||
class Migration(BaseModelMigration): | ||
""" | ||
This migration adds the user_id "-1" to all existing action_workers. | ||
This is the number usually used for calls using the internal route. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. user_id There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Wrote the actual description. |
||
""" | ||
|
||
target_migration_index = 63 | ||
|
||
def migrate_models(self) -> list[BaseRequestEvent] | None: | ||
present_users_per_meeting: dict[int, list[int]] = defaultdict(list) | ||
meetings_per_present_user: dict[int, list[int]] = defaultdict(list) | ||
meetings = self.reader.get_all( | ||
"meeting", ["group_ids", "present_user_ids", "meeting_user_ids"] | ||
) | ||
users = self.reader.get_all( | ||
"user", ["is_present_in_meeting_ids", "meeting_user_ids"] | ||
) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ui, that is a large operation, getting 1000+ meetings and 10000+ users in worse case. Use get_all with cautious There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I thought about this but I couldn't think of a solution, because I have to compare in both directions if the presence relation between the meeting and user is corrupted. If only one way was necessary I could at least filter for all the users that are present in meetings. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Due to refactoring this does not exist anymore. Now I'm using one |
||
|
||
def helper() -> None: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. helper is a bad name. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Changed it. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Due to refactoring this function does not exist anymore. |
||
if not ( | ||
(meeting_user_ids := user.get("meeting_user_ids", [])) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Where does helper get his user from? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Added arguments. Before the nested function would use all local variables in the enclosing scope. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Due to refactoring this function does not exist anymore. |
||
and any( | ||
meeting_user_id in meeting_user_ids | ||
for meeting_user_id in meeting.get("meeting_user_ids", []) | ||
) | ||
): | ||
meetings_per_present_user[user_id].append(meeting_id) | ||
present_users_per_meeting[meeting_id].append(user_id) | ||
|
||
for meeting_id, meeting in meetings.items(): | ||
for user_id in meeting.get("present_user_ids", []): | ||
user = users.get(user_id, dict()) | ||
helper() | ||
|
||
for user_id, user in users.items(): | ||
for meeting_id in user.get("is_present_in_meeting_ids", []): | ||
helper() | ||
|
||
return [ | ||
RequestUpdateEvent( | ||
fqid_from_collection_and_id(what_collection, what_id), | ||
{ | ||
field: [ | ||
id_ | ||
for id_ in lookup.get(what_id, dict()).get(field, []) | ||
if id_ not in which_ids | ||
] | ||
}, | ||
) | ||
for lookup, cross_lookup, what_collection, field in [ | ||
(meetings, present_users_per_meeting, "meeting", "present_user_ids"), | ||
(users, meetings_per_present_user, "user", "is_present_in_meeting_ids"), | ||
] | ||
for what_id, which_ids in cross_lookup.items() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Better to use two loops here. This code no one understands, is bogus. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done. |
||
] |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
from typing import Any | ||
|
||
|
||
def create_data() -> dict[str, dict[str, Any]]: | ||
return { | ||
"meeting/11": { | ||
"id": 1, | ||
"name": "meeting name", | ||
"present_user_ids": [1, 2], | ||
"meeting_user_ids": [3], | ||
}, | ||
"meeting/111": { | ||
"id": 1, | ||
"name": "meeting name", | ||
"present_user_ids": [2], | ||
"meeting_user_ids": [4], | ||
}, | ||
"user/1": { | ||
"id": 1, | ||
"username": "wrong_user", | ||
"is_present_in_meeting_ids": [11], | ||
}, | ||
"user/2": { | ||
"id": 2, | ||
"username": "correct_user", | ||
"is_present_in_meeting_ids": [11, 111], | ||
"meeting_user_ids": [3, 4], | ||
}, | ||
"meeting_user/3": {"id": 3, "user_id": 2, "meeting_id": 11}, | ||
"meeting_user/4": {"id": 4, "user_id": 2, "meeting_id": 111}, | ||
} | ||
|
||
|
||
def test_migration_both_ways(write, finalize, assert_model): | ||
data = create_data() | ||
for fqid, fields in data.items(): | ||
write({"type": "create", "fqid": fqid, "fields": fields}) | ||
|
||
finalize("0062_unset_presence_of_removed_users") | ||
|
||
data["meeting/11"]["present_user_ids"] = [2] | ||
data["user/1"]["is_present_in_meeting_ids"] = [] | ||
|
||
for fqid, fields in data.items(): | ||
assert_model(fqid, fields) | ||
|
||
|
||
def test_migration_one_way(write, finalize, assert_model): | ||
data = create_data() | ||
data["meeting/11"]["present_user_ids"] = [2] | ||
|
||
for fqid, fields in data.items(): | ||
write({"type": "create", "fqid": fqid, "fields": fields}) | ||
|
||
finalize("0062_unset_presence_of_removed_users") | ||
|
||
data["user/1"]["is_present_in_meeting_ids"] = [] | ||
|
||
for fqid, fields in data.items(): | ||
assert_model(fqid, fields) | ||
|
||
|
||
def test_migration_other_way(write, finalize, assert_model): | ||
data = create_data() | ||
data["user/1"]["is_present_in_meeting_ids"] = [] | ||
|
||
for fqid, fields in data.items(): | ||
write({"type": "create", "fqid": fqid, "fields": fields}) | ||
|
||
finalize("0062_unset_presence_of_removed_users") | ||
|
||
data["meeting/11"]["present_user_ids"] = [2] | ||
|
||
for fqid, fields in data.items(): | ||
assert_model(fqid, fields) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As far as I looked at the source code
check_meeting_and_users
is called every time if a user action with the mixin is called. I think, you want to call a presence cleaning only if the user is removed from an meeting or deleted.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've found that at the same condition the 'ConditionalSpeakerCascadeMixin' is executing its 'update_instance'. So I moved it there. I propose to rename this mixin to 'ConditionalMeetingUserCascadeMixin'