morango 0.7.0__tar.gz → 0.7.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of morango might be problematic. Click here for more details.
- {morango-0.7.0 → morango-0.7.1}/CHANGELOG.md +19 -0
- morango-0.7.1/PKG-INFO +58 -0
- {morango-0.7.0 → morango-0.7.1}/morango/__init__.py +1 -1
- morango-0.7.1/morango/management/commands/cleanupsyncs.py +134 -0
- {morango-0.7.0 → morango-0.7.1}/morango/models/core.py +8 -7
- {morango-0.7.0 → morango-0.7.1}/morango/sync/session.py +11 -0
- {morango-0.7.0 → morango-0.7.1}/morango/sync/syncsession.py +7 -2
- morango-0.7.1/morango.egg-info/PKG-INFO +58 -0
- {morango-0.7.0 → morango-0.7.1}/requirements/docs.txt +0 -2
- {morango-0.7.0 → morango-0.7.1}/setup.cfg +1 -0
- morango-0.7.0/PKG-INFO +0 -54
- morango-0.7.0/morango/management/commands/cleanupsyncs.py +0 -84
- morango-0.7.0/morango.egg-info/PKG-INFO +0 -54
- {morango-0.7.0 → morango-0.7.1}/AUTHORS.md +0 -0
- {morango-0.7.0 → morango-0.7.1}/LICENSE +0 -0
- {morango-0.7.0 → morango-0.7.1}/MANIFEST.in +0 -0
- {morango-0.7.0 → morango-0.7.1}/README.md +0 -0
- {morango-0.7.0 → morango-0.7.1}/morango/api/__init__.py +0 -0
- {morango-0.7.0 → morango-0.7.1}/morango/api/fields.py +0 -0
- {morango-0.7.0 → morango-0.7.1}/morango/api/parsers.py +0 -0
- {morango-0.7.0 → morango-0.7.1}/morango/api/permissions.py +0 -0
- {morango-0.7.0 → morango-0.7.1}/morango/api/serializers.py +0 -0
- {morango-0.7.0 → morango-0.7.1}/morango/api/urls.py +0 -0
- {morango-0.7.0 → morango-0.7.1}/morango/api/viewsets.py +0 -0
- {morango-0.7.0 → morango-0.7.1}/morango/apps.py +0 -0
- {morango-0.7.0 → morango-0.7.1}/morango/constants/__init__.py +0 -0
- {morango-0.7.0 → morango-0.7.1}/morango/constants/api_urls.py +0 -0
- {morango-0.7.0 → morango-0.7.1}/morango/constants/capabilities.py +0 -0
- {morango-0.7.0 → morango-0.7.1}/morango/constants/settings.py +0 -0
- {morango-0.7.0 → morango-0.7.1}/morango/constants/transfer_stages.py +0 -0
- {morango-0.7.0 → morango-0.7.1}/morango/constants/transfer_statuses.py +0 -0
- {morango-0.7.0 → morango-0.7.1}/morango/errors.py +0 -0
- {morango-0.7.0 → morango-0.7.1}/morango/management/__init__.py +0 -0
- {morango-0.7.0 → morango-0.7.1}/morango/management/commands/__init__.py +0 -0
- {morango-0.7.0 → morango-0.7.1}/morango/migrations/0001_initial.py +0 -0
- {morango-0.7.0 → morango-0.7.1}/morango/migrations/0002_auto_20170511_0400.py +0 -0
- {morango-0.7.0 → morango-0.7.1}/morango/migrations/0003_auto_20170519_0543.py +0 -0
- {morango-0.7.0 → morango-0.7.1}/morango/migrations/0004_auto_20170520_2112.py +0 -0
- {morango-0.7.0 → morango-0.7.1}/morango/migrations/0005_auto_20170629_2139.py +0 -0
- {morango-0.7.0 → morango-0.7.1}/morango/migrations/0006_instanceidmodel_system_id.py +0 -0
- {morango-0.7.0 → morango-0.7.1}/morango/migrations/0007_auto_20171018_1615.py +0 -0
- {morango-0.7.0 → morango-0.7.1}/morango/migrations/0008_auto_20171114_2217.py +0 -0
- {morango-0.7.0 → morango-0.7.1}/morango/migrations/0009_auto_20171205_0252.py +0 -0
- {morango-0.7.0 → morango-0.7.1}/morango/migrations/0010_auto_20171206_1615.py +0 -0
- {morango-0.7.0 → morango-0.7.1}/morango/migrations/0011_sharedkey.py +0 -0
- {morango-0.7.0 → morango-0.7.1}/morango/migrations/0012_auto_20180927_1658.py +0 -0
- {morango-0.7.0 → morango-0.7.1}/morango/migrations/0013_auto_20190627_1513.py +0 -0
- {morango-0.7.0 → morango-0.7.1}/morango/migrations/0014_syncsession_extra_fields.py +0 -0
- {morango-0.7.0 → morango-0.7.1}/morango/migrations/0015_auto_20200508_2104.py +0 -0
- {morango-0.7.0 → morango-0.7.1}/morango/migrations/0016_store_deserialization_error.py +0 -0
- {morango-0.7.0 → morango-0.7.1}/morango/migrations/0017_store_last_transfer_session_id.py +0 -0
- {morango-0.7.0 → morango-0.7.1}/morango/migrations/0018_auto_20210714_2216.py +0 -0
- {morango-0.7.0 → morango-0.7.1}/morango/migrations/0019_auto_20220113_1807.py +0 -0
- {morango-0.7.0 → morango-0.7.1}/morango/migrations/0020_postgres_fix_nullable.py +0 -0
- {morango-0.7.0 → morango-0.7.1}/morango/migrations/0021_store_partition_index_create.py +0 -0
- {morango-0.7.0 → morango-0.7.1}/morango/migrations/0022_rename_instance_fields.py +0 -0
- {morango-0.7.0 → morango-0.7.1}/morango/migrations/0023_add_instance_id_fields.py +0 -0
- {morango-0.7.0 → morango-0.7.1}/morango/migrations/__init__.py +0 -0
- {morango-0.7.0 → morango-0.7.1}/morango/models/__init__.py +0 -0
- {morango-0.7.0 → morango-0.7.1}/morango/models/certificates.py +0 -0
- {morango-0.7.0 → morango-0.7.1}/morango/models/fields/__init__.py +0 -0
- {morango-0.7.0 → morango-0.7.1}/morango/models/fields/crypto.py +0 -0
- {morango-0.7.0 → morango-0.7.1}/morango/models/fields/uuids.py +0 -0
- {morango-0.7.0 → morango-0.7.1}/morango/models/fsic_utils.py +0 -0
- {morango-0.7.0 → morango-0.7.1}/morango/models/manager.py +0 -0
- {morango-0.7.0 → morango-0.7.1}/morango/models/morango_mptt.py +0 -0
- {morango-0.7.0 → morango-0.7.1}/morango/models/query.py +0 -0
- {morango-0.7.0 → morango-0.7.1}/morango/models/signals.py +0 -0
- {morango-0.7.0 → morango-0.7.1}/morango/models/utils.py +0 -0
- {morango-0.7.0 → morango-0.7.1}/morango/proquint.py +0 -0
- {morango-0.7.0 → morango-0.7.1}/morango/registry.py +0 -0
- {morango-0.7.0 → morango-0.7.1}/morango/sync/__init__.py +0 -0
- {morango-0.7.0 → morango-0.7.1}/morango/sync/backends/__init__.py +0 -0
- {morango-0.7.0 → morango-0.7.1}/morango/sync/backends/base.py +0 -0
- {morango-0.7.0 → morango-0.7.1}/morango/sync/backends/postgres.py +0 -0
- {morango-0.7.0 → morango-0.7.1}/morango/sync/backends/sqlite.py +0 -0
- {morango-0.7.0 → morango-0.7.1}/morango/sync/backends/utils.py +0 -0
- {morango-0.7.0 → morango-0.7.1}/morango/sync/context.py +0 -0
- {morango-0.7.0 → morango-0.7.1}/morango/sync/controller.py +0 -0
- {morango-0.7.0 → morango-0.7.1}/morango/sync/operations.py +0 -0
- {morango-0.7.0 → morango-0.7.1}/morango/sync/utils.py +0 -0
- {morango-0.7.0 → morango-0.7.1}/morango/urls.py +0 -0
- {morango-0.7.0 → morango-0.7.1}/morango/utils.py +0 -0
- {morango-0.7.0 → morango-0.7.1}/morango.egg-info/SOURCES.txt +0 -0
- {morango-0.7.0 → morango-0.7.1}/morango.egg-info/dependency_links.txt +0 -0
- {morango-0.7.0 → morango-0.7.1}/morango.egg-info/not-zip-safe +0 -0
- {morango-0.7.0 → morango-0.7.1}/morango.egg-info/requires.txt +0 -0
- {morango-0.7.0 → morango-0.7.1}/morango.egg-info/top_level.txt +0 -0
- {morango-0.7.0 → morango-0.7.1}/requirements/accelerated.txt +0 -0
- {morango-0.7.0 → morango-0.7.1}/requirements/dev.txt +0 -0
- {morango-0.7.0 → morango-0.7.1}/requirements/postgres.txt +0 -0
- {morango-0.7.0 → morango-0.7.1}/requirements/test.txt +0 -0
- {morango-0.7.0 → morango-0.7.1}/setup.py +0 -0
|
@@ -2,6 +2,25 @@
|
|
|
2
2
|
|
|
3
3
|
List of the most important changes for each release.
|
|
4
4
|
|
|
5
|
+
## 0.7.1
|
|
6
|
+
- Supersedes 0.7.0
|
|
7
|
+
- Drops support for Python 3.4 and 3.5
|
|
8
|
+
- Upgrades cryptography library to 3.3.2, the latest version that supports Python 2.7
|
|
9
|
+
|
|
10
|
+
## 0.6.19
|
|
11
|
+
- The `cleanupsyncs` management command now only cleans up sync sessions if also inactive for `expiration` amount of time
|
|
12
|
+
- Fixes issue accessing index on queryset in `cleanupsyncs` management command
|
|
13
|
+
|
|
14
|
+
## 0.6.18
|
|
15
|
+
- Prevent creation of Deleted and HardDeleted models during deserialization to allow propagation of syncable objects that are recreated after a previous deletion without causing a merge conflict.
|
|
16
|
+
|
|
17
|
+
## 0.6.17
|
|
18
|
+
- Added `client-instance-id`, `server-instance-id`, `sync-filter`, `push` and `pull` arguments to `cleanupsyncs` management command
|
|
19
|
+
- Added option for resuming a sync to ignore the `SyncSession`'s existing `process_id`
|
|
20
|
+
- Added custom user agent to sync HTTP requests
|
|
21
|
+
- Fixed documentation build issues
|
|
22
|
+
- Makefile no longer defines shell with explicit path
|
|
23
|
+
|
|
5
24
|
## 0.6.16
|
|
6
25
|
- Added dedicated `client_instance_id` and `server_instance_id` fields to `SyncSession`
|
|
7
26
|
- Renamed `client_instance` and `server_instance` fields on `SyncSession` to `client_instance_json` and `server_instance_json` respectively
|
morango-0.7.1/PKG-INFO
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: morango
|
|
3
|
+
Version: 0.7.1
|
|
4
|
+
Summary: Pure Python sqlite-based Django DB replication engine.
|
|
5
|
+
Home-page: https://github.com/learningequality/morango
|
|
6
|
+
Author: Learning Equality
|
|
7
|
+
Author-email: dev@learningequality.org
|
|
8
|
+
License: MIT
|
|
9
|
+
Keywords: database,syncing,morango
|
|
10
|
+
Platform: UNKNOWN
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
+
Classifier: Natural Language :: English
|
|
14
|
+
Classifier: Development Status :: 4 - Beta
|
|
15
|
+
Classifier: Programming Language :: Python :: 2.7
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.6
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.7
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
License-File: LICENSE
|
|
24
|
+
License-File: AUTHORS.md
|
|
25
|
+
|
|
26
|
+
# Morango
|
|
27
|
+
|
|
28
|
+
[](https://github.com/learningequality/morango/actions)
|
|
29
|
+
[](http://codecov.io/github/learningequality/morango?branch=master)
|
|
30
|
+
[](http://morango.readthedocs.org/en/latest/)
|
|
31
|
+
|
|
32
|
+
Morango is a pure-Python database replication engine for Django that supports peer-to-peer syncing of data. It is structured as a Django app that can be included in projects to make specific application models syncable.
|
|
33
|
+
|
|
34
|
+
Developed in support of the [Kolibri](https://github.com/learningequality/kolibri) product ecosystem, Morango includes some important features including:
|
|
35
|
+
|
|
36
|
+
- A certificate-based authentication system to protect privacy and integrity of data
|
|
37
|
+
- A change-tracking system to support calculation of differences between databases across low-bandwidth connections
|
|
38
|
+
- A set of constructs to support data partitioning
|
|
39
|
+
|
|
40
|
+
## Developer documentation
|
|
41
|
+
|
|
42
|
+
See [morango.readthedocs.io](https://morango.readthedocs.io)
|
|
43
|
+
|
|
44
|
+
To build and edit the docs, run:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
# install requirements
|
|
48
|
+
pip install -r requirements/docs.txt
|
|
49
|
+
pip install -e .
|
|
50
|
+
|
|
51
|
+
# build docs
|
|
52
|
+
make docs
|
|
53
|
+
|
|
54
|
+
# auto-build and refresh docs on edit
|
|
55
|
+
make docs-autobuild
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import datetime
|
|
2
|
+
import logging
|
|
3
|
+
import uuid
|
|
4
|
+
|
|
5
|
+
from django.core.management.base import BaseCommand
|
|
6
|
+
from django.db import transaction
|
|
7
|
+
from django.utils import timezone
|
|
8
|
+
|
|
9
|
+
from morango.models import SyncSession
|
|
10
|
+
from morango.models import TransferSession
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
logger = logging.getLogger(__name__)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class Command(BaseCommand):
|
|
17
|
+
help = "Closes and cleans up the data for any incomplete sync sessions older than a certain number of hours."
|
|
18
|
+
|
|
19
|
+
def add_arguments(self, parser):
|
|
20
|
+
parser.add_argument(
|
|
21
|
+
"--ids",
|
|
22
|
+
type=lambda ids: ids.split(","),
|
|
23
|
+
default=None,
|
|
24
|
+
help="Comma separated list of SyncSession IDs to filter against"
|
|
25
|
+
)
|
|
26
|
+
parser.add_argument(
|
|
27
|
+
"--expiration",
|
|
28
|
+
action="store",
|
|
29
|
+
type=int,
|
|
30
|
+
default=6,
|
|
31
|
+
help="Number of hours of inactivity after which a session should be considered stale",
|
|
32
|
+
)
|
|
33
|
+
parser.add_argument(
|
|
34
|
+
"--client-instance-id",
|
|
35
|
+
type=uuid.UUID,
|
|
36
|
+
default=None,
|
|
37
|
+
help="Filters the SyncSession models to those with matching 'client_instance_id'",
|
|
38
|
+
)
|
|
39
|
+
parser.add_argument(
|
|
40
|
+
"--server-instance-id",
|
|
41
|
+
type=uuid.UUID,
|
|
42
|
+
default=None,
|
|
43
|
+
help="Filters the SyncSession models to those with matching 'server_instance_id'",
|
|
44
|
+
)
|
|
45
|
+
parser.add_argument(
|
|
46
|
+
"--sync-filter",
|
|
47
|
+
type=str,
|
|
48
|
+
default=None,
|
|
49
|
+
help="Filters the TransferSession models to those with 'filters' starting with 'sync_filter'",
|
|
50
|
+
)
|
|
51
|
+
parser.add_argument(
|
|
52
|
+
"--push",
|
|
53
|
+
type=bool,
|
|
54
|
+
default=None,
|
|
55
|
+
help="Filters the TransferSession models to those with 'push' set to True",
|
|
56
|
+
)
|
|
57
|
+
parser.add_argument(
|
|
58
|
+
"--pull",
|
|
59
|
+
type=bool,
|
|
60
|
+
default=None,
|
|
61
|
+
help="Filters the TransferSession models to those with 'push' set to False",
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
def handle(self, *args, **options):
|
|
65
|
+
|
|
66
|
+
# establish the cutoff time and date for stale sessions
|
|
67
|
+
cutoff = timezone.now() - datetime.timedelta(hours=options["expiration"])
|
|
68
|
+
|
|
69
|
+
sync_sessions = SyncSession.objects.filter(active=True)
|
|
70
|
+
|
|
71
|
+
# if ids arg was passed, filter down sessions to only those IDs
|
|
72
|
+
# if included by expiration filter
|
|
73
|
+
if options["ids"]:
|
|
74
|
+
sync_sessions = sync_sessions.filter(id__in=options["ids"])
|
|
75
|
+
|
|
76
|
+
if options["client_instance_id"]:
|
|
77
|
+
sync_sessions = sync_sessions.filter(client_instance_id=options["client_instance_id"])
|
|
78
|
+
|
|
79
|
+
if options["server_instance_id"]:
|
|
80
|
+
sync_sessions = sync_sessions.filter(server_instance_id=options["server_instance_id"])
|
|
81
|
+
|
|
82
|
+
# retrieve all sessions still marked as active but with no activity since the cutoff
|
|
83
|
+
transfer_sessions = TransferSession.objects.filter(
|
|
84
|
+
sync_session_id__in=sync_sessions.values("id"),
|
|
85
|
+
active=True,
|
|
86
|
+
last_activity_timestamp__lt=cutoff,
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
if options["sync_filter"]:
|
|
90
|
+
transfer_sessions = transfer_sessions.filter(filter__startswith=options["sync_filter"])
|
|
91
|
+
|
|
92
|
+
if options["push"] and not options["pull"]:
|
|
93
|
+
transfer_sessions = transfer_sessions.filter(push=True)
|
|
94
|
+
|
|
95
|
+
if options["pull"] and not options["push"]:
|
|
96
|
+
transfer_sessions = transfer_sessions.filter(push=False)
|
|
97
|
+
|
|
98
|
+
transfer_count = transfer_sessions.count()
|
|
99
|
+
|
|
100
|
+
# loop over the stale sessions one by one to close them out
|
|
101
|
+
for i, transfer_session in enumerate(transfer_sessions):
|
|
102
|
+
logger.info(
|
|
103
|
+
"TransferSession {} of {}: deleting {} Buffers and {} RMC Buffers...".format(
|
|
104
|
+
i + 1,
|
|
105
|
+
transfer_count,
|
|
106
|
+
transfer_session.buffer_set.all().count(),
|
|
107
|
+
transfer_session.recordmaxcounterbuffer_set.all().count(),
|
|
108
|
+
)
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
# delete buffer data and mark as inactive
|
|
112
|
+
with transaction.atomic():
|
|
113
|
+
transfer_session.delete_buffers()
|
|
114
|
+
transfer_session.active = False
|
|
115
|
+
transfer_session.save()
|
|
116
|
+
|
|
117
|
+
# in order to close a sync session, it must have no active transfer sessions
|
|
118
|
+
# and must have no activity since the cutoff
|
|
119
|
+
sync_sessions = sync_sessions.filter(
|
|
120
|
+
last_activity_timestamp__lt=cutoff,
|
|
121
|
+
).exclude(
|
|
122
|
+
transfersession__active=True,
|
|
123
|
+
)
|
|
124
|
+
sync_count = sync_sessions.count()
|
|
125
|
+
|
|
126
|
+
for i, sync_session in enumerate(sync_sessions):
|
|
127
|
+
logger.info(
|
|
128
|
+
"Closing SyncSession {} of {}".format(
|
|
129
|
+
i + 1,
|
|
130
|
+
sync_count,
|
|
131
|
+
)
|
|
132
|
+
)
|
|
133
|
+
sync_session.active = False
|
|
134
|
+
sync_session.save()
|
|
@@ -17,6 +17,7 @@ from django.db.models import F
|
|
|
17
17
|
from django.db.models import Func
|
|
18
18
|
from django.db.models import Max
|
|
19
19
|
from django.db.models import Q
|
|
20
|
+
from django.db.models import signals
|
|
20
21
|
from django.db.models import TextField
|
|
21
22
|
from django.db.models import Value
|
|
22
23
|
from django.db.models.deletion import Collector
|
|
@@ -468,13 +469,13 @@ class Store(AbstractStore):
|
|
|
468
469
|
klass_model = syncable_models.get_model(self.profile, self.model_name)
|
|
469
470
|
# if store model marked as deleted, attempt to delete in app layer
|
|
470
471
|
if self.deleted:
|
|
471
|
-
#
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
472
|
+
# Don't differentiate between deletion and hard deletion here,
|
|
473
|
+
# as we don't want to add additional tracking for models in either case,
|
|
474
|
+
# just to actually delete them.
|
|
475
|
+
# Import here to avoid circular import, as the utils module
|
|
476
|
+
# imports core models.
|
|
477
|
+
from morango.sync.utils import mute_signals
|
|
478
|
+
with mute_signals(signals.post_delete):
|
|
478
479
|
klass_model.objects.filter(id=self.id).delete()
|
|
479
480
|
return None, deferred_fks
|
|
480
481
|
else:
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import logging
|
|
2
2
|
|
|
3
3
|
from requests import exceptions
|
|
4
|
+
from morango import __version__
|
|
4
5
|
from requests.sessions import Session
|
|
5
6
|
from requests.utils import super_len
|
|
6
7
|
from requests.packages.urllib3.util.url import parse_url
|
|
7
8
|
|
|
8
9
|
from morango.utils import serialize_capabilities_to_client_request
|
|
10
|
+
from morango.utils import SETTINGS
|
|
9
11
|
|
|
10
12
|
|
|
11
13
|
logger = logging.getLogger(__name__)
|
|
@@ -35,6 +37,15 @@ class SessionWrapper(Session):
|
|
|
35
37
|
bytes_sent = 0
|
|
36
38
|
bytes_received = 0
|
|
37
39
|
|
|
40
|
+
def __init__(self):
|
|
41
|
+
super(SessionWrapper, self).__init__()
|
|
42
|
+
user_agent_header = "morango/{}".format(__version__)
|
|
43
|
+
if SETTINGS.CUSTOM_INSTANCE_INFO is not None:
|
|
44
|
+
instances = list(SETTINGS.CUSTOM_INSTANCE_INFO)
|
|
45
|
+
if instances:
|
|
46
|
+
user_agent_header += " " + "{}/{}".format(instances[0], SETTINGS.CUSTOM_INSTANCE_INFO.get(instances[0]))
|
|
47
|
+
self.headers["User-Agent"] = "{} {}".format(user_agent_header, self.headers["User-Agent"])
|
|
48
|
+
|
|
38
49
|
def request(self, method, url, **kwargs):
|
|
39
50
|
response = None
|
|
40
51
|
try:
|
|
@@ -260,12 +260,16 @@ class NetworkSyncConnection(Connection):
|
|
|
260
260
|
sync_session = SyncSession.objects.create(**data)
|
|
261
261
|
return SyncSessionClient(self, sync_session)
|
|
262
262
|
|
|
263
|
-
def resume_sync_session(self, sync_session_id, chunk_size=None):
|
|
263
|
+
def resume_sync_session(self, sync_session_id, chunk_size=None, ignore_existing_process=False):
|
|
264
264
|
"""
|
|
265
265
|
Resumes an existing sync session given an ID
|
|
266
266
|
|
|
267
267
|
:param sync_session_id: The UUID of the `SyncSession` to resume
|
|
268
268
|
:param chunk_size: An optional parameter specifying the size for each transferred chunk
|
|
269
|
+
:type chunk_size: int
|
|
270
|
+
:param ignore_existing_process:An optional parameter specifying whether to ignore an
|
|
271
|
+
existing active process ID
|
|
272
|
+
:type ignore_existing_process: bool
|
|
269
273
|
:return: A SyncSessionClient instance
|
|
270
274
|
:rtype: SyncSessionClient
|
|
271
275
|
"""
|
|
@@ -281,7 +285,8 @@ class NetworkSyncConnection(Connection):
|
|
|
281
285
|
|
|
282
286
|
# check that process of existing session isn't still running
|
|
283
287
|
if (
|
|
284
|
-
|
|
288
|
+
not ignore_existing_process
|
|
289
|
+
and sync_session.process_id
|
|
285
290
|
and sync_session.process_id != os.getpid()
|
|
286
291
|
and pid_exists(sync_session.process_id)
|
|
287
292
|
):
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: morango
|
|
3
|
+
Version: 0.7.1
|
|
4
|
+
Summary: Pure Python sqlite-based Django DB replication engine.
|
|
5
|
+
Home-page: https://github.com/learningequality/morango
|
|
6
|
+
Author: Learning Equality
|
|
7
|
+
Author-email: dev@learningequality.org
|
|
8
|
+
License: MIT
|
|
9
|
+
Keywords: database,syncing,morango
|
|
10
|
+
Platform: UNKNOWN
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
+
Classifier: Natural Language :: English
|
|
14
|
+
Classifier: Development Status :: 4 - Beta
|
|
15
|
+
Classifier: Programming Language :: Python :: 2.7
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.6
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.7
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
License-File: LICENSE
|
|
24
|
+
License-File: AUTHORS.md
|
|
25
|
+
|
|
26
|
+
# Morango
|
|
27
|
+
|
|
28
|
+
[](https://github.com/learningequality/morango/actions)
|
|
29
|
+
[](http://codecov.io/github/learningequality/morango?branch=master)
|
|
30
|
+
[](http://morango.readthedocs.org/en/latest/)
|
|
31
|
+
|
|
32
|
+
Morango is a pure-Python database replication engine for Django that supports peer-to-peer syncing of data. It is structured as a Django app that can be included in projects to make specific application models syncable.
|
|
33
|
+
|
|
34
|
+
Developed in support of the [Kolibri](https://github.com/learningequality/kolibri) product ecosystem, Morango includes some important features including:
|
|
35
|
+
|
|
36
|
+
- A certificate-based authentication system to protect privacy and integrity of data
|
|
37
|
+
- A change-tracking system to support calculation of differences between databases across low-bandwidth connections
|
|
38
|
+
- A set of constructs to support data partitioning
|
|
39
|
+
|
|
40
|
+
## Developer documentation
|
|
41
|
+
|
|
42
|
+
See [morango.readthedocs.io](https://morango.readthedocs.io)
|
|
43
|
+
|
|
44
|
+
To build and edit the docs, run:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
# install requirements
|
|
48
|
+
pip install -r requirements/docs.txt
|
|
49
|
+
pip install -e .
|
|
50
|
+
|
|
51
|
+
# build docs
|
|
52
|
+
make docs
|
|
53
|
+
|
|
54
|
+
# auto-build and refresh docs on edit
|
|
55
|
+
make docs-autobuild
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
|
morango-0.7.0/PKG-INFO
DELETED
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.1
|
|
2
|
-
Name: morango
|
|
3
|
-
Version: 0.7.0
|
|
4
|
-
Summary: Pure Python sqlite-based Django DB replication engine.
|
|
5
|
-
Home-page: https://github.com/learningequality/morango
|
|
6
|
-
Author: Learning Equality
|
|
7
|
-
Author-email: dev@learningequality.org
|
|
8
|
-
License: MIT
|
|
9
|
-
Description: # Morango
|
|
10
|
-
|
|
11
|
-
[](https://github.com/learningequality/morango/actions)
|
|
12
|
-
[](http://codecov.io/github/learningequality/morango?branch=master)
|
|
13
|
-
[](http://morango.readthedocs.org/en/latest/)
|
|
14
|
-
|
|
15
|
-
Morango is a pure-Python database replication engine for Django that supports peer-to-peer syncing of data. It is structured as a Django app that can be included in projects to make specific application models syncable.
|
|
16
|
-
|
|
17
|
-
Developed in support of the [Kolibri](https://github.com/learningequality/kolibri) product ecosystem, Morango includes some important features including:
|
|
18
|
-
|
|
19
|
-
- A certificate-based authentication system to protect privacy and integrity of data
|
|
20
|
-
- A change-tracking system to support calculation of differences between databases across low-bandwidth connections
|
|
21
|
-
- A set of constructs to support data partitioning
|
|
22
|
-
|
|
23
|
-
## Developer documentation
|
|
24
|
-
|
|
25
|
-
See [morango.readthedocs.io](https://morango.readthedocs.io)
|
|
26
|
-
|
|
27
|
-
To build and edit the docs, run:
|
|
28
|
-
|
|
29
|
-
```bash
|
|
30
|
-
# install requirements
|
|
31
|
-
pip install -r requirements/docs.txt
|
|
32
|
-
pip install -e .
|
|
33
|
-
|
|
34
|
-
# build docs
|
|
35
|
-
make docs
|
|
36
|
-
|
|
37
|
-
# auto-build and refresh docs on edit
|
|
38
|
-
make docs-autobuild
|
|
39
|
-
```
|
|
40
|
-
|
|
41
|
-
Keywords: database,syncing,morango
|
|
42
|
-
Platform: UNKNOWN
|
|
43
|
-
Classifier: Intended Audience :: Developers
|
|
44
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
45
|
-
Classifier: Natural Language :: English
|
|
46
|
-
Classifier: Development Status :: 4 - Beta
|
|
47
|
-
Classifier: Programming Language :: Python :: 2.7
|
|
48
|
-
Classifier: Programming Language :: Python :: 3.6
|
|
49
|
-
Classifier: Programming Language :: Python :: 3.7
|
|
50
|
-
Classifier: Programming Language :: Python :: 3.8
|
|
51
|
-
Classifier: Programming Language :: Python :: 3.9
|
|
52
|
-
Classifier: Programming Language :: Python :: 3.10
|
|
53
|
-
Classifier: Programming Language :: Python :: 3.11
|
|
54
|
-
Description-Content-Type: text/markdown
|
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
import datetime
|
|
2
|
-
import logging
|
|
3
|
-
|
|
4
|
-
from django.core.management.base import BaseCommand
|
|
5
|
-
from django.db import transaction
|
|
6
|
-
from django.utils import timezone
|
|
7
|
-
|
|
8
|
-
from morango.models import SyncSession
|
|
9
|
-
from morango.models import TransferSession
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
logger = logging.getLogger(__name__)
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
class Command(BaseCommand):
|
|
16
|
-
help = "Closes and cleans up the data for any incomplete sync sessions older than a certain number of hours."
|
|
17
|
-
|
|
18
|
-
def add_arguments(self, parser):
|
|
19
|
-
parser.add_argument(
|
|
20
|
-
"--ids",
|
|
21
|
-
type=lambda ids: ids.split(","),
|
|
22
|
-
default=None,
|
|
23
|
-
help="Comma separated list of SyncSession IDs to filter against"
|
|
24
|
-
)
|
|
25
|
-
parser.add_argument(
|
|
26
|
-
"--expiration",
|
|
27
|
-
action="store",
|
|
28
|
-
type=int,
|
|
29
|
-
default=6,
|
|
30
|
-
help="Number of hours of inactivity after which a session should be considered stale",
|
|
31
|
-
)
|
|
32
|
-
|
|
33
|
-
def handle(self, *args, **options):
|
|
34
|
-
|
|
35
|
-
# establish the cutoff time and date for stale sessions
|
|
36
|
-
cutoff = timezone.now() - datetime.timedelta(hours=options["expiration"])
|
|
37
|
-
|
|
38
|
-
sync_sessions = SyncSession.objects.filter(active=True)
|
|
39
|
-
|
|
40
|
-
# if ids arg was passed, filter down sessions to only those IDs if included by expiration filter
|
|
41
|
-
if options["ids"]:
|
|
42
|
-
sync_sessions = sync_sessions.filter(id__in=options["ids"])
|
|
43
|
-
|
|
44
|
-
# retrieve all sessions still marked as active but with no activity since the cutoff
|
|
45
|
-
transfer_sessions = TransferSession.objects.filter(
|
|
46
|
-
sync_session_id__in=sync_sessions.values("id"),
|
|
47
|
-
active=True,
|
|
48
|
-
last_activity_timestamp__lt=cutoff,
|
|
49
|
-
)
|
|
50
|
-
|
|
51
|
-
transfer_count = transfer_sessions.count()
|
|
52
|
-
|
|
53
|
-
# loop over the stale sessions one by one to close them out
|
|
54
|
-
for i in range(transfer_count):
|
|
55
|
-
transfer_session = transfer_sessions[0]
|
|
56
|
-
logger.info(
|
|
57
|
-
"TransferSession {} of {}: deleting {} Buffers and {} RMC Buffers...".format(
|
|
58
|
-
i + 1,
|
|
59
|
-
transfer_count,
|
|
60
|
-
transfer_session.buffer_set.all().count(),
|
|
61
|
-
transfer_session.recordmaxcounterbuffer_set.all().count(),
|
|
62
|
-
)
|
|
63
|
-
)
|
|
64
|
-
|
|
65
|
-
# delete buffer data and mark as inactive
|
|
66
|
-
with transaction.atomic():
|
|
67
|
-
transfer_session.delete_buffers()
|
|
68
|
-
transfer_session.active = False
|
|
69
|
-
transfer_session.save()
|
|
70
|
-
|
|
71
|
-
sync_count = sync_sessions.count()
|
|
72
|
-
|
|
73
|
-
# finally loop over sync sessions and close out if there are no other active transfer sessions
|
|
74
|
-
for i in range(sync_count):
|
|
75
|
-
sync_session = sync_sessions[0]
|
|
76
|
-
if not sync_session.transfersession_set.filter(active=True).exists():
|
|
77
|
-
logger.info(
|
|
78
|
-
"Closing SyncSession {} of {}".format(
|
|
79
|
-
i + 1,
|
|
80
|
-
sync_count,
|
|
81
|
-
)
|
|
82
|
-
)
|
|
83
|
-
sync_session.active = False
|
|
84
|
-
sync_session.save()
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.1
|
|
2
|
-
Name: morango
|
|
3
|
-
Version: 0.7.0
|
|
4
|
-
Summary: Pure Python sqlite-based Django DB replication engine.
|
|
5
|
-
Home-page: https://github.com/learningequality/morango
|
|
6
|
-
Author: Learning Equality
|
|
7
|
-
Author-email: dev@learningequality.org
|
|
8
|
-
License: MIT
|
|
9
|
-
Description: # Morango
|
|
10
|
-
|
|
11
|
-
[](https://github.com/learningequality/morango/actions)
|
|
12
|
-
[](http://codecov.io/github/learningequality/morango?branch=master)
|
|
13
|
-
[](http://morango.readthedocs.org/en/latest/)
|
|
14
|
-
|
|
15
|
-
Morango is a pure-Python database replication engine for Django that supports peer-to-peer syncing of data. It is structured as a Django app that can be included in projects to make specific application models syncable.
|
|
16
|
-
|
|
17
|
-
Developed in support of the [Kolibri](https://github.com/learningequality/kolibri) product ecosystem, Morango includes some important features including:
|
|
18
|
-
|
|
19
|
-
- A certificate-based authentication system to protect privacy and integrity of data
|
|
20
|
-
- A change-tracking system to support calculation of differences between databases across low-bandwidth connections
|
|
21
|
-
- A set of constructs to support data partitioning
|
|
22
|
-
|
|
23
|
-
## Developer documentation
|
|
24
|
-
|
|
25
|
-
See [morango.readthedocs.io](https://morango.readthedocs.io)
|
|
26
|
-
|
|
27
|
-
To build and edit the docs, run:
|
|
28
|
-
|
|
29
|
-
```bash
|
|
30
|
-
# install requirements
|
|
31
|
-
pip install -r requirements/docs.txt
|
|
32
|
-
pip install -e .
|
|
33
|
-
|
|
34
|
-
# build docs
|
|
35
|
-
make docs
|
|
36
|
-
|
|
37
|
-
# auto-build and refresh docs on edit
|
|
38
|
-
make docs-autobuild
|
|
39
|
-
```
|
|
40
|
-
|
|
41
|
-
Keywords: database,syncing,morango
|
|
42
|
-
Platform: UNKNOWN
|
|
43
|
-
Classifier: Intended Audience :: Developers
|
|
44
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
45
|
-
Classifier: Natural Language :: English
|
|
46
|
-
Classifier: Development Status :: 4 - Beta
|
|
47
|
-
Classifier: Programming Language :: Python :: 2.7
|
|
48
|
-
Classifier: Programming Language :: Python :: 3.6
|
|
49
|
-
Classifier: Programming Language :: Python :: 3.7
|
|
50
|
-
Classifier: Programming Language :: Python :: 3.8
|
|
51
|
-
Classifier: Programming Language :: Python :: 3.9
|
|
52
|
-
Classifier: Programming Language :: Python :: 3.10
|
|
53
|
-
Classifier: Programming Language :: Python :: 3.11
|
|
54
|
-
Description-Content-Type: text/markdown
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|