@@ -86,64 +86,128 @@ def fetch_remote_folders(
86
86
provider : str , crispin_client : CrispinClient
87
87
) -> Iterable [RemoteFolder ]:
88
88
try :
89
- folder_names = crispin_client .folder_names ()
89
+ folders = crispin_client .folders ()
90
90
except Exception :
91
91
return
92
92
93
- for role , folders in folder_names . items ( ):
94
- if provider == "gmail" and role not in ["all" , "spam" , "trash" ]:
93
+ for folder in sorted ( folders , key = lambda f : f . display_name ):
94
+ if provider == "gmail" and folder . role not in ["all" , "spam" , "trash" ]:
95
95
continue
96
96
97
- for folder in folders :
98
- try :
99
- result = crispin_client .select_folder (
100
- folder , lambda _account_id , _folder_name , select_info : select_info
101
- )
102
- except Exception :
103
- continue
104
-
105
- yield RemoteFolder (
106
- name = folder ,
107
- role = role ,
108
- uidnext = result [b"UIDNEXT" ],
109
- exists = result [b"EXISTS" ],
97
+ try :
98
+ result = crispin_client .select_folder (
99
+ folder .display_name ,
100
+ lambda _account_id , _folder_name , select_info : select_info ,
110
101
)
102
+ except Exception :
103
+ continue
104
+
105
+ yield RemoteFolder (
106
+ name = folder .display_name ,
107
+ role = folder .role ,
108
+ uidnext = result [b"UIDNEXT" ],
109
+ exists = result [b"EXISTS" ],
110
+ )
111
111
112
112
113
113
@dataclasses .dataclass
114
114
class LocalFolder :
115
115
id : int
116
116
name : str
117
117
state : str
118
- uidnext : int
118
+ uidmax : int
119
119
exists : int
120
120
121
121
122
122
def fetch_local_folders (account : LocalAccount ) -> Iterable [LocalFolder ]:
123
123
with global_session_scope () as db_session :
124
- for folder in db_session .query (Folder ).filter (Folder .account_id == account .id ):
124
+ for folder in (
125
+ db_session .query (Folder )
126
+ .filter (Folder .account_id == account .id )
127
+ .order_by (Folder .name )
128
+ ):
125
129
exists = (
126
130
db_session .query (ImapUid ).filter (ImapUid .folder_id == folder .id ).count ()
127
131
)
128
- uidnext = (
129
- (
130
- db_session .query (ImapUid .msg_uid )
131
- .filter (ImapUid .folder_id == folder .id )
132
- .order_by (ImapUid .msg_uid .desc ())
133
- .limit (1 )
134
- .scalar ()
135
- )
136
- or 0
137
- ) + 1
132
+ uidmax = (
133
+ db_session .query (ImapUid .msg_uid )
134
+ .filter (ImapUid .folder_id == folder .id )
135
+ .order_by (ImapUid .msg_uid .desc ())
136
+ .limit (1 )
137
+ .scalar ()
138
+ ) or 0
138
139
yield LocalFolder (
139
140
id = folder .id ,
140
141
name = folder .name ,
141
142
state = folder .imapsyncstatus .state ,
142
- uidnext = uidnext ,
143
+ uidmax = uidmax ,
143
144
exists = exists ,
144
145
)
145
146
146
147
148
+ @dataclasses .dataclass
149
+ class SummarizedList :
150
+ value : list
151
+ max_values : int = 10
152
+
153
+ def __repr__ (self ):
154
+ if len (self .value ) <= self .max_values :
155
+ return repr (self .value )
156
+
157
+ return f"[{ self .value [0 ]} , ... ,{ self .value [- 1 ]} len={ len (self .value )} ]"
158
+
159
+
160
+ @dataclasses .dataclass
161
+ class LocalFolderDiff :
162
+ name : str
163
+ uids_to_add : list [int ]
164
+ uids_to_delete : list [int ]
165
+
166
+
167
+ @dataclasses .dataclass
168
+ class LocalFolderMissing :
169
+ name : str
170
+
171
+
172
+ def compare_local_and_remote (
173
+ crispin_client : CrispinClient ,
174
+ remote_folders : list [RemoteFolder ],
175
+ local_folders : list [LocalFolder ],
176
+ ):
177
+ remote_folders_by_name = {folder .name : folder for folder in remote_folders }
178
+ local_folders_by_name = {folder .name : folder for folder in local_folders }
179
+
180
+ for name , remote_folder in remote_folders_by_name .items ():
181
+ local_folder = local_folders_by_name .get (name )
182
+ if not local_folder :
183
+ yield LocalFolderMissing (name = name )
184
+
185
+ if local_folder .exists == remote_folder .exists :
186
+ continue
187
+
188
+ crispin_client .select_folder (
189
+ local_folder .name ,
190
+ lambda _account_id , _folder_name , select_info : select_info ,
191
+ )
192
+ remote_uids = set (crispin_client .all_uids ())
193
+ with global_session_scope () as db_session :
194
+ local_uids = set (
195
+ uid
196
+ for uid , in db_session .query (ImapUid .msg_uid ).filter (
197
+ ImapUid .folder_id == local_folder .id
198
+ )
199
+ )
200
+
201
+ uids_to_add = remote_uids - local_uids
202
+ uids_to_delete = local_uids - remote_uids
203
+
204
+ yield LocalFolderDiff (
205
+ name = local_folder .name ,
206
+ uids_to_add = SummarizedList (sorted (uids_to_add )),
207
+ uids_to_delete = SummarizedList (sorted (uids_to_delete )),
208
+ )
209
+
210
+
147
211
@click .command ()
148
212
@click .option ("--host" , default = None )
149
213
@click .option ("--account-id" , default = None )
@@ -163,18 +227,22 @@ def main(host: "str | None", account_id: "str | None", include_server_info: bool
163
227
print ()
164
228
165
229
total_folder_remote_exists = 0
230
+ remote_folders = []
166
231
for remote_folder in fetch_remote_folders (
167
232
account .provider , crispin_client
168
233
):
169
234
print ("\t " , remote_folder )
235
+ remote_folders .append (remote_folder )
170
236
total_folder_remote_exists += remote_folder .exists
171
237
total_remote_exists += remote_folder .exists
172
238
print ("\t Total remote EXISTS:" , total_folder_remote_exists )
173
239
print ()
174
240
175
241
total_folder_local_exists = 0
242
+ local_folders = []
176
243
for local_folder in fetch_local_folders (account ):
177
244
print ("\t " , local_folder )
245
+ local_folders .append (local_folder )
178
246
total_folder_local_exists += local_folder .exists
179
247
total_local_exists += local_folder .exists
180
248
print ("\t Total local EXISTS:" , total_folder_local_exists )
@@ -183,6 +251,12 @@ def main(host: "str | None", account_id: "str | None", include_server_info: bool
183
251
total_folder_remote_exists - total_folder_local_exists ,
184
252
)
185
253
print ()
254
+
255
+ for diff in compare_local_and_remote (
256
+ crispin_client , remote_folders , local_folders
257
+ ):
258
+ print ("\t " , diff )
259
+ print ()
186
260
except Exception as e :
187
261
print ("\t Exception opening the connection" , e )
188
262
print ()
0 commit comments