mirror of
https://github.com/zulip/zulip.git
synced 2025-11-11 09:27:43 +00:00
export: Handle more tables via export_from_config().
This commit introduces the ability to do custom fetches and to essentially use temp tables for intermediate results. (The temp table stuff deals with recipients/subscriptions having three different flavors--user, stream, and huddle.)
This commit is contained in:
@@ -360,6 +360,12 @@ def get_realm_config():
|
|||||||
|
|
||||||
def get_admin_auth_config(realm_config):
|
def get_admin_auth_config(realm_config):
|
||||||
# type: (Config) -> Config
|
# type: (Config) -> Config
|
||||||
|
|
||||||
|
# To export only some users, you can tweak the below UserProfile config
|
||||||
|
# to give the target users, but then you should create any users not
|
||||||
|
# being exported in a separate
|
||||||
|
# response['zerver_userprofile_mirrordummy'] export so that
|
||||||
|
# conversations with those users can still be exported.
|
||||||
user_profile_config = Config(
|
user_profile_config = Config(
|
||||||
table='zerver_userprofile',
|
table='zerver_userprofile',
|
||||||
model=UserProfile,
|
model=UserProfile,
|
||||||
@@ -368,6 +374,14 @@ def get_admin_auth_config(realm_config):
|
|||||||
exclude=['password', 'api_key'],
|
exclude=['password', 'api_key'],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
Config(
|
||||||
|
custom_tables=[
|
||||||
|
'zerver_userprofile_crossrealm',
|
||||||
|
],
|
||||||
|
virtual_parent=user_profile_config,
|
||||||
|
custom_fetch=fetch_user_profile_cross_realm,
|
||||||
|
)
|
||||||
|
|
||||||
Config(
|
Config(
|
||||||
table='zerver_userpresence',
|
table='zerver_userpresence',
|
||||||
model=UserPresence,
|
model=UserPresence,
|
||||||
@@ -397,80 +411,86 @@ def get_admin_auth_config(realm_config):
|
|||||||
parent_key='realm_id__in',
|
parent_key='realm_id__in',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
stream_config = Config(
|
||||||
|
table='zerver_stream',
|
||||||
|
model=Stream,
|
||||||
|
normal_parent=realm_config,
|
||||||
|
virtual_parent=user_profile_config,
|
||||||
|
parent_key='realm_id__in',
|
||||||
|
exclude=['email_token'],
|
||||||
|
)
|
||||||
|
|
||||||
|
# Some of these tables are intermediate "tables" that we
|
||||||
|
# create only for the export. Think of them as similar to views.
|
||||||
|
user_recipient_config = Config(
|
||||||
|
table='_user_recipient',
|
||||||
|
model=Recipient,
|
||||||
|
filter_args={'type': Recipient.PERSONAL},
|
||||||
|
normal_parent=user_profile_config,
|
||||||
|
parent_key='type_id__in', # Note! (This is not a typical fk.)
|
||||||
|
)
|
||||||
|
|
||||||
|
Config(
|
||||||
|
table='_user_subscription',
|
||||||
|
model=Subscription,
|
||||||
|
normal_parent=user_recipient_config,
|
||||||
|
parent_key='recipient_id__in',
|
||||||
|
)
|
||||||
|
|
||||||
|
#
|
||||||
|
|
||||||
|
stream_recipient_config = Config(
|
||||||
|
table='_stream_recipient',
|
||||||
|
model=Recipient,
|
||||||
|
filter_args={'type': Recipient.STREAM},
|
||||||
|
normal_parent=stream_config,
|
||||||
|
parent_key='type_id__in',
|
||||||
|
)
|
||||||
|
|
||||||
|
Config(
|
||||||
|
table='_stream_subscription',
|
||||||
|
model=Subscription,
|
||||||
|
normal_parent=stream_recipient_config,
|
||||||
|
parent_key='recipient_id__in',
|
||||||
|
)
|
||||||
|
|
||||||
|
#
|
||||||
|
|
||||||
|
Config(
|
||||||
|
custom_tables=[
|
||||||
|
'_huddle_recipient',
|
||||||
|
'_huddle_subscription',
|
||||||
|
'zerver_huddle',
|
||||||
|
],
|
||||||
|
normal_parent=user_profile_config,
|
||||||
|
custom_fetch=fetch_huddle_objects,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Now build permanent tables from our temp tables.
|
||||||
|
Config(
|
||||||
|
table='zerver_recipient',
|
||||||
|
virtual_parent=user_profile_config,
|
||||||
|
concat_and_destroy=[
|
||||||
|
'_user_recipient',
|
||||||
|
'_stream_recipient',
|
||||||
|
'_huddle_recipient',
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
Config(
|
||||||
|
table='zerver_subscription',
|
||||||
|
virtual_parent=user_profile_config,
|
||||||
|
concat_and_destroy=[
|
||||||
|
'_user_subscription',
|
||||||
|
'_stream_subscription',
|
||||||
|
'_huddle_subscription',
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
return user_profile_config
|
return user_profile_config
|
||||||
|
|
||||||
# To export only some users, you can tweak the below UserProfile query
|
def fetch_user_profile_cross_realm(response, config, context):
|
||||||
# to give the target users, but then you should create any users not
|
# type: (TableData, Config, Context) -> None
|
||||||
# being exported in a separate
|
|
||||||
# response['zerver_userprofile_mirrordummy'] export so that
|
|
||||||
# conversations with those users can still be exported.
|
|
||||||
def export_with_admin_auth(realm, response, admin_auth_config, include_invite_only=True):
|
|
||||||
# type: (Realm, TableData, Config, bool) -> None
|
|
||||||
|
|
||||||
def get_primary_ids(records):
|
|
||||||
# type: (List[Record]) -> Set[int]
|
|
||||||
return set(x['id'] for x in records)
|
|
||||||
|
|
||||||
# Note that the filter_by_foo functions aren't composable--it shouldn't
|
|
||||||
# be an issue; for complex filtering, just use the ORM more directly.
|
|
||||||
|
|
||||||
def filter_by_realm(model, **kwargs):
|
|
||||||
# type: (Any, **Any) -> Any
|
|
||||||
return model.objects.filter(realm=realm, **kwargs)
|
|
||||||
|
|
||||||
# This will populate response['zerver_userprofile'] and some
|
|
||||||
# other user-related tables.
|
|
||||||
export_from_config(
|
|
||||||
response=response,
|
|
||||||
config=admin_auth_config
|
|
||||||
)
|
|
||||||
cross_realm_context = {'realm': realm}
|
|
||||||
fetch_user_profile_cross_realm(response, cross_realm_context)
|
|
||||||
|
|
||||||
user_profile_ids = get_primary_ids(response['zerver_userprofile'])
|
|
||||||
|
|
||||||
|
|
||||||
user_recipient_query = Recipient.objects.filter(type=Recipient.PERSONAL,
|
|
||||||
type_id__in=user_profile_ids)
|
|
||||||
user_recipients = make_raw(user_recipient_query)
|
|
||||||
user_recipient_ids = get_primary_ids(user_recipients)
|
|
||||||
|
|
||||||
|
|
||||||
def filter_by_users(model, **kwargs):
|
|
||||||
# type: (Any, **Any) -> Any
|
|
||||||
return model.objects.filter(user_profile__in=user_profile_ids, **kwargs)
|
|
||||||
|
|
||||||
|
|
||||||
user_subscription_query = filter_by_users(Subscription,
|
|
||||||
recipient_id__in=user_recipient_ids)
|
|
||||||
user_subscription_dicts = make_raw(user_subscription_query)
|
|
||||||
|
|
||||||
stream_query = filter_by_realm(Stream)
|
|
||||||
if not include_invite_only:
|
|
||||||
stream_query = stream_query.filter(invite_only=False)
|
|
||||||
response['zerver_stream'] = [model_to_dict(x, exclude=["email_token"]) for x in stream_query]
|
|
||||||
floatify_datetime_fields(response, 'zerver_stream')
|
|
||||||
stream_ids = get_primary_ids(response['zerver_stream'])
|
|
||||||
|
|
||||||
stream_recipient_query = Recipient.objects.filter(type=Recipient.STREAM,
|
|
||||||
type_id__in=stream_ids)
|
|
||||||
stream_recipients = make_raw(stream_recipient_query)
|
|
||||||
stream_recipient_ids = get_primary_ids(stream_recipients)
|
|
||||||
|
|
||||||
stream_subscription_query = filter_by_users(Subscription,
|
|
||||||
recipient_id__in=stream_recipient_ids)
|
|
||||||
stream_subscription_dicts = make_raw(stream_subscription_query)
|
|
||||||
|
|
||||||
|
|
||||||
context = {'realm': realm, 'user_profile_ids': user_profile_ids}
|
|
||||||
huddle_subscription_dicts, huddle_recipients = fetch_huddle_objects(response, context)
|
|
||||||
|
|
||||||
response["zerver_recipient"] = user_recipients + stream_recipients + huddle_recipients
|
|
||||||
response["zerver_subscription"] = user_subscription_dicts + stream_subscription_dicts + huddle_subscription_dicts
|
|
||||||
|
|
||||||
def fetch_user_profile_cross_realm(response, context):
|
|
||||||
# type: (TableData, Context) -> None
|
|
||||||
|
|
||||||
realm = context['realm']
|
realm = context['realm']
|
||||||
|
|
||||||
if realm.domain == "zulip.com":
|
if realm.domain == "zulip.com":
|
||||||
@@ -482,13 +502,11 @@ def fetch_user_profile_cross_realm(response, context):
|
|||||||
get_user_profile_by_email(settings.WELCOME_BOT),
|
get_user_profile_by_email(settings.WELCOME_BOT),
|
||||||
]]
|
]]
|
||||||
|
|
||||||
def fetch_huddle_objects(response, context):
|
def fetch_huddle_objects(response, config, context):
|
||||||
# type: (TableData, Context) -> Tuple[List[Record], List[Record]]
|
# type: (TableData, Config, Context) -> None
|
||||||
|
|
||||||
# We introduce a context variable here as a bit of pre-factoring
|
|
||||||
# for upcoming changes.
|
|
||||||
realm = context['realm']
|
realm = context['realm']
|
||||||
user_profile_ids = context['user_profile_ids']
|
user_profile_ids = set(r['id'] for r in response[config.parent.table])
|
||||||
|
|
||||||
# First we get all huddles involving someone in the realm.
|
# First we get all huddles involving someone in the realm.
|
||||||
realm_huddle_subs = Subscription.objects.select_related("recipient").filter(recipient__type=Recipient.HUDDLE,
|
realm_huddle_subs = Subscription.objects.select_related("recipient").filter(recipient__type=Recipient.HUDDLE,
|
||||||
@@ -514,9 +532,10 @@ def fetch_huddle_objects(response, context):
|
|||||||
|
|
||||||
huddle_subscription_dicts = make_raw(huddle_subs)
|
huddle_subscription_dicts = make_raw(huddle_subs)
|
||||||
huddle_recipients = make_raw(Recipient.objects.filter(id__in=huddle_recipient_ids))
|
huddle_recipients = make_raw(Recipient.objects.filter(id__in=huddle_recipient_ids))
|
||||||
response['zerver_huddle'] = make_raw(Huddle.objects.filter(id__in=huddle_ids))
|
|
||||||
|
|
||||||
return (huddle_subscription_dicts, huddle_recipients)
|
response['_huddle_recipient'] = huddle_recipients
|
||||||
|
response['_huddle_subscription'] = huddle_subscription_dicts
|
||||||
|
response['zerver_huddle'] = make_raw(Huddle.objects.filter(id__in=huddle_ids))
|
||||||
|
|
||||||
def fetch_usermessages(realm, message_ids, user_profile_ids, message_filename):
|
def fetch_usermessages(realm, message_ids, user_profile_ids, message_filename):
|
||||||
# type: (Realm, Set[int], Set[int], Path) -> List[Record]
|
# type: (Realm, Set[int], Set[int], Path) -> List[Record]
|
||||||
@@ -844,11 +863,19 @@ def do_export_realm(realm, output_dir, threads):
|
|||||||
response=response,
|
response=response,
|
||||||
config=realm_config,
|
config=realm_config,
|
||||||
seed_object=realm,
|
seed_object=realm,
|
||||||
|
context=dict(realm=realm)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
admin_auth_config = get_admin_auth_config(realm_config)
|
||||||
|
|
||||||
logging.info("Exporting core realm data")
|
logging.info("Exporting core realm data")
|
||||||
admin_auth_config = get_admin_auth_config(realm_config)
|
admin_auth_config = get_admin_auth_config(realm_config)
|
||||||
export_with_admin_auth(realm, response, admin_auth_config)
|
export_from_config(
|
||||||
|
response=response,
|
||||||
|
config=admin_auth_config,
|
||||||
|
context=dict(realm=realm)
|
||||||
|
)
|
||||||
|
|
||||||
export_file = os.path.join(output_dir, "realm.json")
|
export_file = os.path.join(output_dir, "realm.json")
|
||||||
write_data_to_file(output_file=export_file, data=response)
|
write_data_to_file(output_file=export_file, data=response)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user