Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
11f41df
Improve DB query logic in 3 endpoints which were causing performance …
varmar05 May 1, 2026
cda6585
Add admin panel enhancements according to #3283
May 4, 2026
6e4b393
More fixes to avoid N+1, mostly in serialization schemas
varmar05 May 5, 2026
28a2c83
Fix tests
varmar05 May 5, 2026
9a88a5a
Update code with use of reusables, fix some checks
May 15, 2026
414817f
Fix some prettier issues
May 15, 2026
681537d
Merge branch 'master' into fix_db_performance_issues
varmar05 May 20, 2026
78c62b9
Add slot for WS table in admin for user profile
May 20, 2026
59902de
Merge remote-tracking branch 'origin/develop' into implement-3283
May 24, 2026
005bfc0
Update compiled type declarations for useDataTableSearch and TableDat…
May 24, 2026
196a8a9
Merge pull request #617 from MerginMaps/fix_db_performance_issues
MarcelGeo May 25, 2026
1f2d949
Fix case sensitivity issue in project lookup
varmar05 May 27, 2026
44b0742
Merge pull request #629 from MerginMaps/implement-#3284
MarcelGeo May 28, 2026
b269291
Chore: add CI for alembic migration tests
varmar05 May 29, 2026
5765de4
Add configurable confirm dialog logo and update Project Settings
Jun 1, 2026
c70625a
Replace boolean value
Jun 1, 2026
4693764
Merge pull request #618 from MerginMaps/implement-3283
MarcelGeo Jun 1, 2026
6b9aba4
Make some naming changes
Jun 1, 2026
599e2d7
Merge pull request #633 from MerginMaps/fix_project_by_names_lookup
MarcelGeo Jun 3, 2026
f18e219
Add classes for invitation action icons
harminius Jun 5, 2026
659b08a
Merge pull request #636 from MerginMaps/resend_invitation
MarcelGeo Jun 5, 2026
c1fda54
Merge remote-tracking branch 'origin/master' into alembic_tests
varmar05 Jun 5, 2026
ead4fbf
Merge pull request #634 from MerginMaps/alembic_tests
MarcelGeo Jun 5, 2026
b43bf71
Merge pull request #637 from MerginMaps/master
MarcelGeo Jun 5, 2026
f678b1d
Merge pull request #635 from MerginMaps/implement-#3345
MarcelGeo Jun 8, 2026
69195ab
Suppress focus halo on icon action buttons after mouse click
harminius Jun 12, 2026
cf44156
Merge pull request #638 from MerginMaps/suppress_icon_action_btn_focu…
MarcelGeo Jun 17, 2026
3ff5dc2
Apply focus halo suppression to all PrimeVue buttons
harminius Jun 18, 2026
e0968d5
Merge pull request #641 from MerginMaps/global_button_focus_halo
MarcelGeo Jun 18, 2026
56e867c
Merge pull request #642 from MerginMaps/develop
MarcelGeo Jun 23, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 18 additions & 3 deletions .github/workflows/auto_tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,18 @@ name: Auto Tests
on: push

jobs:
server_tests:
tests:
runs-on: ubuntu-24.04

strategy:
fail-fast: false
matrix:
include:
- suite: server
pytest_args: "-v --cov=mergin --cov-report=lcov mergin/tests"
- suite: migration
pytest_args: "-v mergin/test_migrations"

services:
postgres:
image: postgres:14
Expand All @@ -15,13 +24,18 @@ jobs:
POSTGRES_USER: postgres
ports:
- 5435:5432
# Set health checks to wait until postgres has started
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5

env:
DB_USER: postgres
DB_PASSWORD: postgres
DB_HOST: localhost
DB_PORT: 5435

steps:
- name: Check out repository
uses: actions/checkout@v3
Expand All @@ -36,9 +50,10 @@ jobs:
- name: Run tests
run: |
cd server
pipenv run pytest -v --cov=mergin --cov-report=lcov mergin/tests
pipenv run pytest ${{ matrix.pytest_args }}

- name: Coveralls
if: matrix.suite == 'server'
uses: coverallsapp/github-action@v2
with:
base-path: server
Expand Down
6 changes: 6 additions & 0 deletions server/mergin/sync/interfaces.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,12 @@ def get_by_name(self, name):
"""
pass

def get_by_names(self, names):
"""
Return list of workspaces whose names are in the given collection.
"""
pass

@abstractmethod
def get_by_project(self, project):
"""
Expand Down
48 changes: 45 additions & 3 deletions server/mergin/sync/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import json
import logging
import os
import re
import threading
import time
import uuid
Expand All @@ -18,7 +19,9 @@
from blinker import signal
from flask_login import current_user
from pygeodiff import GeoDiff
from functools import cached_property
from sqlalchemy import text, null, desc, nullslast, tuple_
from sqlalchemy.orm import contains_eager, joinedload, load_only
from sqlalchemy.dialects.postgresql import ARRAY, BIGINT, UUID, JSONB, ENUM, insert
from sqlalchemy.types import String
from sqlalchemy.ext.hybrid import hybrid_property
Expand Down Expand Up @@ -136,6 +139,12 @@ def workspace(self):
project_workspace = current_app.ws_handler.get(self.workspace_id)
return project_workspace

@cached_property
def _has_conflict(self) -> bool:
"""True if any current project file matches a known conflict-copy pattern."""
pattern = r"(\.gpkg|\.qgs|.qgz)(.*conflict.*)|( \(.*conflict.*)"
return any(re.search(pattern, f.path) for f in self.files)

def get_latest_files_cache(self) -> List[int]:
"""Get latest file history ids either from cached table or calculate them on the fly"""
if self.latest_project_files.file_history_ids is not None:
Expand Down Expand Up @@ -658,7 +667,7 @@ def __init__(
def path(self) -> str:
return self.file.path

@property
@cached_property
def diff(self) -> Optional[FileDiff]:
"""Diff file pushed with UPDATE_DIFF change type.

Expand Down Expand Up @@ -713,9 +722,37 @@ def changes(
if not (is_versioned_file(file) and since is not None and to is not None):
return []

history = []
# when since=1 the range spans the entire project history; narrow it to
# the most recent CREATE/DELETE so we don't load records from previous
# file lifecycles that the Python break would discard anyway
if since == 1:
boundary = (
FileHistory.query.join(ProjectFilePath)
.filter(
ProjectFilePath.project_id == project_id,
ProjectFilePath.path == file,
FileHistory.project_version_name <= to,
FileHistory.change.in_(
[PushChangeType.CREATE.value, PushChangeType.DELETE.value]
),
)
.order_by(desc(FileHistory.project_version_name))
.with_entities(FileHistory.project_version_name)
.first()
)
since = boundary[0] if boundary else since

full_history = (
FileHistory.query.join(ProjectFilePath)
.join(FileHistory.version)
.join(ProjectVersion.project)
.options(
contains_eager(FileHistory.file).load_only(ProjectFilePath.path),
contains_eager(FileHistory.version)
.load_only(ProjectVersion.name, ProjectVersion.project_id)
.contains_eager(ProjectVersion.project)
.load_only(Project.storage_params),
)
.filter(
ProjectFilePath.project_id == project_id,
FileHistory.project_version_name <= to,
Expand All @@ -726,6 +763,7 @@ def changes(
.all()
)

history = []
for item in full_history:
history.append(item)

Expand Down Expand Up @@ -1781,11 +1819,15 @@ def diff_summary(self):

def changes_count(self) -> Dict:
"""Return number of changes by type"""
query = f"SELECT change, COUNT(change) FROM file_history WHERE version_id = :version_id GROUP BY change;"
query = "SELECT change, COUNT(change) FROM file_history WHERE version_id = :version_id GROUP BY change;"
params = {"version_id": self.id}
result = db.session.execute(text(query), params).fetchall()
return {row[0]: row[1] for row in result}

@cached_property
def _changes_count(self) -> Dict:
return self.changes_count()

@property
def zip_path(self):
return os.path.join(
Expand Down
5 changes: 0 additions & 5 deletions server/mergin/sync/public_api.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1124,11 +1124,6 @@ components:
- added
- updated
- removed
expiration:
nullable: true
type: string
format: date-time
example: 2019-02-26T08:47:58.636074Z
UploadFileInfo:
allOf:
- $ref: "#/components/schemas/FileInfo"
Expand Down
Loading
Loading