1
+ # -*- coding: utf-8 -*-
2
+
3
+ # GPLv3 license
4
+ # Copyright Lutra Consulting Limited
5
+
1
6
from math import floor
2
7
3
8
try :
38
43
mergin_project_local_path ,
39
44
PROJS_PER_PAGE ,
40
45
same_dir ,
46
+ get_local_mergin_projects_info ,
41
47
)
42
48
from .utils_auth import AuthTokenExpiredError
43
49
from .mergin .merginproject import MerginProject
44
50
45
- MERGIN_CLIENT_LOG = os .path .join (QgsApplication .qgisSettingsDirPath (), "mergin-client-log.txt" )
46
- os .environ ["MERGIN_CLIENT_LOG" ] = MERGIN_CLIENT_LOG
47
-
48
51
49
52
class MerginRemoteProjectItem (QgsDataItem ):
50
53
"""Data item to represent a remote Mergin Maps project."""
@@ -56,16 +59,7 @@ def __init__(self, parent, project, project_manager, plugin):
56
59
project ["namespace" ], project ["name" ]
57
60
) # we need posix path for server API calls
58
61
display_name = project ["name" ]
59
- group_items = project_manager .get_mergin_browser_groups ()
60
- if group_items .get ("Shared with me" ) == parent :
61
- display_name = self .project_name
62
- QgsDataItem .__init__ (
63
- self ,
64
- QgsDataItem .Collection ,
65
- parent ,
66
- display_name ,
67
- "/Mergin/" + self .project_name ,
68
- )
62
+ QgsDataItem .__init__ (self , QgsDataItem .Collection , parent , display_name , "/Mergin/" + self .project_name )
69
63
self .path = None
70
64
self .setSortKey (f"1 { self .name ()} " )
71
65
self .setIcon (QIcon (icon_path ("cloud.svg" )))
@@ -103,10 +97,6 @@ def clone_remote_project(self):
103
97
msg = "Mergin Maps project cloned successfully."
104
98
QMessageBox .information (None , "Clone project" , msg , QMessageBox .StandardButton .Close )
105
99
self .parent ().reload ()
106
- # we also need to reload My projects group as the cloned project could appear there
107
- group_items = self .project_manager .get_mergin_browser_groups ()
108
- if "My projects" in group_items :
109
- group_items ["My projects" ].reload ()
110
100
111
101
def remove_remote_project (self ):
112
102
dlg = RemoveProjectDialog (self .project ["namespace" ], self .project ["name" ])
@@ -151,9 +141,6 @@ def __init__(self, parent, project, project_manager, plugin):
151
141
self .project_name = posixpath .join (project ["namespace" ], project ["name" ]) # posix path for server API calls
152
142
self .path = mergin_project_local_path (self .project_name )
153
143
display_name = project ["name" ]
154
- group_items = project_manager .get_mergin_browser_groups ()
155
- if group_items .get ("Shared with me" ) == parent :
156
- display_name = self .project_name
157
144
QgsDirectoryItem .__init__ (self , parent , display_name , self .path , "/Mergin/" + self .project_name )
158
145
self .setSortKey (f"0 { self .name ()} " )
159
146
self .project = project
@@ -224,7 +211,7 @@ def remove_local_project(self):
224
211
# as releasing lock on previously open files takes some time
225
212
# we have to wait a bit before removing them, otherwise rmtree
226
213
# will fail and removal of the local rpoject will fail as well
227
- QTimer .singleShot (250 , lambda : shutil .rmtree (self .path ))
214
+ QTimer .singleShot (500 , lambda : shutil .rmtree (self .path ))
228
215
except PermissionError as e :
229
216
QgsApplication .messageLog ().logMessage (f"Mergin Maps plugin: { str (e )} " )
230
217
msg = (
@@ -359,6 +346,7 @@ def __init__(
359
346
self .error = ""
360
347
self .wizard = None
361
348
self .projects = []
349
+ self .local_projects = []
362
350
self .total_projects_count = None
363
351
self .fetch_more_item = None
364
352
self .create_new_project_item = None
@@ -372,16 +360,16 @@ def update_client_and_manager(self, mc=None, manager=None, err=None):
372
360
self .project_manager = manager
373
361
self .error = err
374
362
self .projects = []
363
+ self .local_projects = []
375
364
self .updateName ()
376
365
self .depopulate ()
377
366
378
367
def updateName (self ):
379
368
name = self .base_name
380
369
try :
381
- if self .mc .server_type () != ServerType .OLD and self .plugin .current_workspace .get ("name" , None ):
382
- name = f"{ self .base_name } [{ self .plugin .current_workspace ['name' ]} ]"
383
- except AttributeError :
384
- # self.mc might not be set yet
370
+ name = f"{ self .base_name } [{ self .plugin .current_workspace ['name' ]} ]"
371
+ except KeyError :
372
+ # self.mc might not be set yet or there is no workspace
385
373
pass
386
374
self .setName (name )
387
375
@@ -396,84 +384,74 @@ def createChildren(self):
396
384
sip .transferto (error_item , self )
397
385
return [error_item ]
398
386
399
- if self .mc .server_type () == ServerType .OLD :
400
- return self .createChildrenGroups ()
401
-
402
387
return self .createChildrenProjects ()
403
388
404
389
def createChildrenProjects (self ):
405
390
if not self .projects :
406
391
error = self .fetch_projects ()
407
392
if error is not None :
408
393
return error
394
+ if not self .local_projects :
395
+ self .local_projects = [
396
+ {"namespace" : i [1 ], "name" : i [2 ]}
397
+ for i in get_local_mergin_projects_info (self .plugin .current_workspace ["name" ])
398
+ ]
409
399
items = []
410
- for project in self .projects :
411
- project_name = posixpath .join (project ["namespace" ], project ["name" ]) # posix path for server API calls
412
- local_proj_path = mergin_project_local_path (project_name )
413
- if local_proj_path is None or not os .path .exists (local_proj_path ):
414
- item = MerginRemoteProjectItem (self , project , self .project_manager , self .plugin )
415
- item .setState (QgsDataItem .Populated ) # make it non-expandable
416
- else :
417
- item = MerginLocalProjectItem (self , project , self .project_manager , self .plugin )
400
+
401
+ # build a set of (namespace, name) tuples for quick lookup
402
+ local_keys = {(p ["namespace" ], p ["name" ]) for p in self .local_projects }
403
+ # projects not present locally
404
+ remote_only = [p for p in self .projects if (p ["namespace" ], p ["name" ]) not in local_keys ]
405
+
406
+ for project in self .local_projects :
407
+ item = MerginLocalProjectItem (self , project , self .project_manager , self .plugin )
408
+ sip .transferto (item , self )
409
+ items .append (item )
410
+
411
+ for project in remote_only :
412
+ item = MerginRemoteProjectItem (self , project , self .project_manager , self .plugin )
413
+ item .setState (QgsDataItem .Populated ) # make it non-expandable
418
414
sip .transferto (item , self )
419
415
items .append (item )
420
416
self .set_fetch_more_item ()
421
417
if self .fetch_more_item is not None :
422
418
items .append (self .fetch_more_item )
423
- if not items and self . mc . server_type () != ServerType . OLD :
419
+ if not items :
424
420
self .create_new_project_item = CreateNewProjectItem (self )
425
421
self .create_new_project_item .setState (QgsDataItem .Populated )
426
422
sip .transferto (self .create_new_project_item , self )
427
423
items .append (self .create_new_project_item )
428
424
return items
429
425
430
- def createChildrenGroups (self ):
431
- items = []
432
- my_projects = MerginGroupItem (self , "My projects" , "created" , "user.svg" , 1 , self .plugin )
433
- my_projects .setState (QgsDataItem .Populated )
434
- my_projects .refresh ()
435
- sip .transferto (my_projects , self )
436
- items .append (my_projects )
437
-
438
- shared_projects = MerginGroupItem (self , "Shared with me" , "shared" , "users.svg" , 2 , self .plugin )
439
- shared_projects .setState (QgsDataItem .Populated )
440
- shared_projects .refresh ()
441
- sip .transferto (shared_projects , self )
442
- items .append (shared_projects )
443
-
444
- return items
445
-
446
426
def fetch_projects (self , page = 1 , per_page = PROJS_PER_PAGE ):
447
427
"""Get paginated projects list from Mergin Maps service. If anything goes wrong, return an error item."""
448
428
if self .project_manager is None :
449
- error_item = QgsErrorItem (
450
- self ,
451
- "Failed to log in. Please check the configuration" ,
452
- "/Mergin/error" ,
453
- )
429
+ error_item = QgsErrorItem (self , "Failed to log in. Please check the configuration" , "/Mergin/error" )
454
430
sip .transferto (error_item , self )
455
431
return [error_item ]
456
- if self . mc . server_type () != ServerType . OLD and not self .plugin .current_workspace :
432
+ if not self .plugin .current_workspace :
457
433
error_item = QgsErrorItem (self , "No workspace available" , "/Mergin/error" )
458
434
sip .transferto (error_item , self )
459
435
return [error_item ]
460
436
try :
461
- if self .mc .server_type () == ServerType .OLD :
462
- resp = self .project_manager .mc .paginated_projects_list (
463
- flag = self .filter ,
464
- page = page ,
465
- per_page = per_page ,
466
- order_params = "namespace_asc,name_asc" ,
467
- )
468
- else :
469
- resp = self .project_manager .mc .paginated_projects_list (
470
- only_namespace = self .plugin .current_workspace .get ("name" , None ),
471
- page = page ,
472
- per_page = per_page ,
473
- order_params = "name_asc" ,
474
- )
437
+ resp = self .project_manager .mc .paginated_projects_list (
438
+ only_namespace = self .plugin .current_workspace .get ("name" , None ),
439
+ page = page ,
440
+ per_page = per_page ,
441
+ order_params = "name_asc" ,
442
+ )
475
443
self .projects += resp ["projects" ]
476
444
self .total_projects_count = int (resp ["count" ]) if is_number (resp ["count" ]) else 0
445
+
446
+ # Sometimes we fetched a local, recursivly fetch until we fetched enougth at the same time
447
+ set_fetched_projects = set ([i ["name" ] for i in resp ["projects" ]])
448
+ set_local_projects = set ([i ["name" ] for i in self .local_projects ])
449
+
450
+ new_projs_per_page_left = per_page - len (set_fetched_projects - set_local_projects )
451
+ if new_projs_per_page_left != 0 and len (self .projects ) < self .total_projects_count :
452
+ new_page_to_get = floor (len (self .projects ) / new_projs_per_page_left ) + 1
453
+ self .fetch_projects (new_page_to_get , per_page = new_projs_per_page_left )
454
+
477
455
except URLError :
478
456
error_item = QgsErrorItem (self , "Failed to get projects from server" , "/Mergin/error" )
479
457
sip .transferto (error_item , self )
@@ -497,16 +475,13 @@ def set_fetch_more_item(self):
497
475
self .fetch_more_item = FetchMoreItem (self )
498
476
self .fetch_more_item .setState (QgsDataItem .Populated )
499
477
sip .transferto (self .fetch_more_item , self )
500
- if isinstance (self , MerginGroupItem ):
501
- group_name = f"{ self .base_name } ({ self .total_projects_count } )"
502
- self .setName (group_name )
503
478
504
479
def fetch_more (self ):
505
480
"""Fetch another page of projects and add them to the group item."""
506
481
if self .fetch_more_item is None :
507
482
QMessageBox .information (None , "Fetch Mergin Maps Projects" , "All projects already listed." )
508
483
return
509
- page_to_get = floor (self .rowCount ( ) / PROJS_PER_PAGE ) + 1
484
+ page_to_get = floor (len ( self .projects ) / PROJS_PER_PAGE ) + 1
510
485
dummy = self .fetch_projects (page = page_to_get )
511
486
self .refresh ()
512
487
@@ -515,6 +490,10 @@ def reload(self):
515
490
self .plugin .choose_active_workspace ()
516
491
517
492
self .projects = []
493
+ self .local_projects = [
494
+ {"namespace" : i [1 ], "name" : i [2 ]}
495
+ for i in get_local_mergin_projects_info (self .plugin .current_workspace ["name" ])
496
+ ]
518
497
self .refresh ()
519
498
520
499
def new_project (self ):
@@ -546,10 +525,7 @@ def actions(self, parent):
546
525
actions = [action_configure ]
547
526
if self .mc :
548
527
server_type = self .mc .server_type ()
549
- if server_type == ServerType .OLD :
550
- actions .append (action_create )
551
- actions .append (action_explore )
552
- elif server_type == ServerType .CE :
528
+ if server_type == ServerType .CE :
553
529
actions .append (action_refresh )
554
530
actions .append (action_create )
555
531
actions .append (action_find )
@@ -563,33 +539,6 @@ def actions(self, parent):
563
539
return actions
564
540
565
541
566
- class MerginGroupItem (MerginRootItem ):
567
- """Mergin group data item. Contains filtered list of Mergin Maps projects."""
568
-
569
- def __init__ (self , parent , grp_name , grp_filter , icon , order , plugin ):
570
- MerginRootItem .__init__ (self , parent , grp_name , grp_filter , icon , order , plugin )
571
-
572
- def isMerginGroupItem (self ):
573
- return True
574
-
575
- def createChildren (self ):
576
- return self .createChildrenProjects ()
577
-
578
- def actions (self , parent ):
579
- action_refresh = QAction (QIcon (icon_path ("repeat.svg" )), "Reload" , parent )
580
- action_refresh .triggered .connect (self .reload )
581
- actions = [action_refresh ]
582
- if self .fetch_more_item is not None :
583
- action_fetch_more = QAction (QIcon (icon_path ("dots.svg" )), "Fetch more" , parent )
584
- action_fetch_more .triggered .connect (self .fetch_more )
585
- actions .append (action_fetch_more )
586
- if self .name ().startswith ("My projects" ):
587
- action_create = QAction (QIcon (icon_path ("square-plus.svg" )), "Create new project" , parent )
588
- action_create .triggered .connect (self .new_project )
589
- actions .append (action_create )
590
- return actions
591
-
592
-
593
542
class DataItemProvider (QgsDataItemProvider ):
594
543
def __init__ (self , plugin ):
595
544
QgsDataItemProvider .__init__ (self )
0 commit comments