Skip to content

Commit e930e84

Browse files
committed
LocalFolderDiff
1 parent ceae07d commit e930e84

File tree

1 file changed

+103
-29
lines changed

1 file changed

+103
-29
lines changed

bin/inbox-summary.py

+103-29
Original file line numberDiff line numberDiff line change
@@ -86,64 +86,128 @@ def fetch_remote_folders(
8686
provider: str, crispin_client: CrispinClient
8787
) -> Iterable[RemoteFolder]:
8888
try:
89-
folder_names = crispin_client.folder_names()
89+
folders = crispin_client.folders()
9090
except Exception:
9191
return
9292

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"]:
9595
continue
9696

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,
110101
)
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+
)
111111

112112

113113
@dataclasses.dataclass
114114
class LocalFolder:
115115
id: int
116116
name: str
117117
state: str
118-
uidnext: int
118+
uidmax: int
119119
exists: int
120120

121121

122122
def fetch_local_folders(account: LocalAccount) -> Iterable[LocalFolder]:
123123
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+
):
125129
exists = (
126130
db_session.query(ImapUid).filter(ImapUid.folder_id == folder.id).count()
127131
)
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
138139
yield LocalFolder(
139140
id=folder.id,
140141
name=folder.name,
141142
state=folder.imapsyncstatus.state,
142-
uidnext=uidnext,
143+
uidmax=uidmax,
143144
exists=exists,
144145
)
145146

146147

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+
147211
@click.command()
148212
@click.option("--host", default=None)
149213
@click.option("--account-id", default=None)
@@ -163,18 +227,22 @@ def main(host: "str | None", account_id: "str | None", include_server_info: bool
163227
print()
164228

165229
total_folder_remote_exists = 0
230+
remote_folders = []
166231
for remote_folder in fetch_remote_folders(
167232
account.provider, crispin_client
168233
):
169234
print("\t", remote_folder)
235+
remote_folders.append(remote_folder)
170236
total_folder_remote_exists += remote_folder.exists
171237
total_remote_exists += remote_folder.exists
172238
print("\t Total remote EXISTS:", total_folder_remote_exists)
173239
print()
174240

175241
total_folder_local_exists = 0
242+
local_folders = []
176243
for local_folder in fetch_local_folders(account):
177244
print("\t", local_folder)
245+
local_folders.append(local_folder)
178246
total_folder_local_exists += local_folder.exists
179247
total_local_exists += local_folder.exists
180248
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
183251
total_folder_remote_exists - total_folder_local_exists,
184252
)
185253
print()
254+
255+
for diff in compare_local_and_remote(
256+
crispin_client, remote_folders, local_folders
257+
):
258+
print("\t", diff)
259+
print()
186260
except Exception as e:
187261
print("\t Exception opening the connection", e)
188262
print()

0 commit comments

Comments
 (0)