def sync_trigger_partial_sync_callback(req, service): svc = Service.FromID(service) if req.method == "POST": # if whe're using decathlon services, force resync # Get users ids list, depending of services response = svc.ExternalIDsForPartialSyncTrigger(req) _sync = Sync() # Get users _id list from external ID users_to_sync = _sync.getUsersIDFromExternalId(response, service) if not users_to_sync: return HttpResponse(status=401) else: for user in users_to_sync: # For each users, if we can sync now if "LastSynchronization" in user and user["LastSynchronization"] is not None and datetime.utcnow() - \ user["LastSynchronization"] < _sync.MinimumSyncInterval: return HttpResponse(status=403) exhaustive = None if "LastSynchronization" in user and user["LastSynchronization"] is not None and datetime.utcnow() - \ user["LastSynchronization"] > _sync.MaximumIntervalBeforeExhaustiveSync: exhaustive = True # Force immadiate sync _sync.ScheduleImmediateSync(user, exhaustive) return HttpResponse(status=204) elif req.method == "GET": return svc.PartialSyncTriggerGET(req) else: return HttpResponse(status=400)
def sync_clear_errorgroup(req, service, group): if not req.user: return HttpResponse(status=401) rec = User.GetConnectionRecord(req.user, service) if not rec: return HttpResponse(status=404) # Prevent this becoming a vehicle for rapid synchronization to_clear_count = 0 for x in rec.SyncErrors: if "UserException" in x and "ClearGroup" in x["UserException"] and x[ "UserException"]["ClearGroup"] == group: to_clear_count += 1 if to_clear_count > 0: db.connections.update( {"_id": rec._id}, {"$pull": { "SyncErrors": { "UserException.ClearGroup": group } }}) db.users.update( {"_id": req.user["_id"]}, {'$inc': { "BlockingSyncErrorCount": -to_clear_count }} ) # In the interests of data integrity, update the summary counts immediately as opposed to waiting for a sync to complete. Sync.ScheduleImmediateSync( req.user, True ) # And schedule them for an immediate full resynchronization, so the now-unblocked services can be brought up to speed. return HttpResponse() return HttpResponse() return HttpResponse(status=404)
def ConfigurationUpdating(self, svcRec, newConfig, oldConfig): from tapiriik.sync import Sync from tapiriik.auth import User if newConfig["SyncRoot"] != oldConfig["SyncRoot"]: Sync.ScheduleImmediateSync(User.AuthByService(svcRec), True) cachedb.dropbox_cache.update({"ExternalID": svcRec.ExternalID}, {"$unset": { "Structure": None }})
def _assocPaymentLikeObject(user, collection, payment_like_object, schedule_now, skip_deassoc=False): # Since I seem to have taken this duck-typing quite far # First, deassociate payment ids from other accounts that may be using them if "_id" in payment_like_object and not skip_deassoc: db.users.update({}, {"$pull": {collection: {"_id": payment_like_object["_id"]}}}, multi=True) # Then, attach to us db.users.update({"_id": ObjectId(user["_id"])}, {"$addToSet": {collection: payment_like_object}}) if schedule_now: Sync.ScheduleImmediateSync(user)
def sync_schedule_immediate(req): if not req.user: return HttpResponse(status=401) if "LastSynchronization" in req.user and req.user["LastSynchronization"] is not None and datetime.utcnow() - req.user["LastSynchronization"] < Sync.MinimumSyncInterval: return HttpResponse(status=403) exhaustive = None if "LastSynchronization" in req.user and req.user["LastSynchronization"] is not None and datetime.utcnow() - req.user["LastSynchronization"] > Sync.MaximumIntervalBeforeExhaustiveSync: exhaustive = True Sync.ScheduleImmediateSync(req.user, exhaustive) return HttpResponse()
def schedule_immediate_rc_sync(req): token = req.POST.get('token') if token is None: return HttpResponse(status=403) user = User.EnsureWithRcToken(req, token) uid, authData, extendedAuthData = (token, {}, {"token": token}) serviceRecord = Service.EnsureServiceRecordWithAuth(RunnersConnectService, uid, authData, extendedAuthData, True) User.ConnectService(user, serviceRecord) #if "LastSynchronization" in req.user and req.user["LastSynchronization"] is not None and datetime.utcnow() - req.user["LastSynchronization"] < Sync.MinimumSyncInterval: # return HttpResponse(status=429) exhaustive = None #if "LastSynchronization" in req.user and req.user["LastSynchronization"] is not None and datetime.utcnow() - req.user["LastSynchronization"] > Sync.MaximumIntervalBeforeExhaustiveSync: # exhaustive = True Sync.ScheduleImmediateSync(req.user, exhaustive) return HttpResponse()
def AssociatePayment(user, payment): db.users.update( {"_id": { '$ne': ObjectId(user["_id"]) }}, { "$pull": { "Payments": { "_id": payment["_id"] if "_id" in payment else None } } }, multi=True ) # deassociate payment ids from other accounts that may be using them db.users.update({"_id": ObjectId(user["_id"])}, {"$addToSet": { "Payments": payment }}) Sync.ScheduleImmediateSync(user)
def ConnectService(user, serviceRecord): from tapiriik.services import Service, UserExceptionType existingUser = db.users.find_one({ "_id": { '$ne': ObjectId(user["_id"]) }, "ConnectedServices.ID": ObjectId(serviceRecord._id) }) if "ConnectedServices" not in user: user["ConnectedServices"] = [] delta = False if existingUser is not None: # merge merge merge # Don't let the user end up with two services of the same type, ever # It's not fully supported, plus it's caused all sorts of trauma in the past. # Note that this will discard the new serviceRecord connection if an existing one exists on the other account # ...which isn't the end of the world, compared to screwing around asking the user which they wanted to keep. for to_merge_service in existingUser["ConnectedServices"]: if len([ x for x in user["ConnectedServices"] if x["Service"] == to_merge_service["Service"] ]) == 0: user["ConnectedServices"].append(to_merge_service) # There's got to be some 1-liner to do this merge if "Payments" in existingUser: if "Payments" not in user: user["Payments"] = [] user["Payments"] += existingUser["Payments"] if "Promos" in existingUser: if "Promos" not in user: user["Promos"] = [] user["Promos"] += existingUser["Promos"] if "ExternalPayments" in existingUser: if "ExternalPayments" not in user: user["ExternalPayments"] = [] user["ExternalPayments"] += existingUser["ExternalPayments"] if "FlowExceptions" in existingUser: if "FlowExceptions" not in user: user["FlowExceptions"] = [] user["FlowExceptions"] += existingUser["FlowExceptions"] user["Email"] = user["Email"] if "Email" in user and user[ "Email"] is not None else ( existingUser["Email"] if "Email" in existingUser else None) user["NonblockingSyncErrorCount"] = ( user["NonblockingSyncErrorCount"] if "NonblockingSyncErrorCount" in user and user["NonblockingSyncErrorCount"] is not None else 0 ) + (existingUser["NonblockingSyncErrorCount"] if "NonblockingSyncErrorCount" in existingUser and existingUser["NonblockingSyncErrorCount"] is not None else 0) user["BlockingSyncErrorCount"] = ( user["BlockingSyncErrorCount"] if "BlockingSyncErrorCount" in user and user["BlockingSyncErrorCount"] is not None else 0 ) + (existingUser["BlockingSyncErrorCount"] if "BlockingSyncErrorCount" in existingUser and existingUser["BlockingSyncErrorCount"] is not None else 0) user["SyncExclusionCount"] = ( user["SyncExclusionCount"] if "SyncExclusionCount" in user and user["SyncExclusionCount"] is not None else 0) + ( existingUser["SyncExclusionCount"] if "SyncExclusionCount" in existingUser and existingUser["SyncExclusionCount"] is not None else 0) user[ "Created"] = user["Created"] if user["Created"] < existingUser[ "Created"] else existingUser["Created"] if "AncestorAccounts" not in user: user["AncestorAccounts"] = [] user["AncestorAccounts"] += existingUser["AncestorAccounts"] if "AncestorAccounts" in existingUser else [] user["AncestorAccounts"] += [existingUser["_id"]] user["Timezone"] = user["Timezone"] if "Timezone" in user and user[ "Timezone"] else (existingUser["Timezone"] if "Timezone" in existingUser else None) user["CreationIP"] = user[ "CreationIP"] if "CreationIP" in user and user[ "CreationIP"] else (existingUser["CreationIP"] if "CreationIP" in existingUser else None) existing_config = existingUser[ "Config"] if "Config" in existingUser else {} existing_config.update(user["Config"] if "Config" in user else {}) user["Config"] = existing_config delta = True db.users.remove({"_id": existingUser["_id"]}) else: if serviceRecord._id not in [ x["ID"] for x in user["ConnectedServices"] ]: # we might be connecting a second account for the same service for duplicateConn in [ x for x in user["ConnectedServices"] if x["Service"] == serviceRecord.Service.ID ]: dupeRecord = User.GetConnectionRecord( user, serviceRecord.Service.ID ) # this'll just pick the first connection of type, but we repeat the right # of times anyways Service.DeleteServiceRecord(dupeRecord) # We used to call DisconnectService() here, but the results of that call were getting overwritten, which was unfortunate. user["ConnectedServices"] = [ x for x in user["ConnectedServices"] if x["Service"] != serviceRecord.Service.ID ] user["ConnectedServices"].append({ "Service": serviceRecord.Service.ID, "ID": serviceRecord._id }) delta = True db.users.update({"_id": user["_id"]}, user) if delta or ( hasattr(serviceRecord, "SyncErrors") and len(serviceRecord.SyncErrors) > 0 ): # also schedule an immediate sync if there is an outstanding error (i.e. user reconnected) db.connections.update( {"_id": serviceRecord._id}, { "$pull": { "SyncErrors": { "UserException.Type": UserExceptionType.Authorization } } } ) # Pull all auth-related errors from the service so they don't continue to see them while the sync completes. db.connections.update( {"_id": serviceRecord._id}, { "$pull": { "SyncErrors": { "UserException.Type": UserExceptionType.RenewPassword } } } ) # Pull all auth-related errors from the service so they don't continue to see them while the sync completes. Sync.SetNextSyncIsExhaustive( user, True ) # exhaustive, so it'll pick up activities from newly added services / ones lost during an error if hasattr(serviceRecord, "SyncErrors") and len(serviceRecord.SyncErrors) > 0: Sync.ScheduleImmediateSync(user)
def diag_user(req, user): try: userRec = db.users.find_one({"_id": ObjectId(user)}) except: userRec = None if not userRec: searchOpts = [{"Payments.Txn": user}, {"Payments.Email": user}] try: searchOpts.append({"AncestorAccounts": ObjectId(user)}) searchOpts.append({"ConnectedServices.ID": ObjectId(user)}) except: pass # Invalid format for ObjectId userRec = db.users.find_one({"$or": searchOpts}) if not userRec: searchOpts = [{"ExternalID": user}] try: searchOpts.append({"ExternalID": int(user)}) except: pass # Not an int svcRec = db.connections.find_one({"$or": searchOpts}) if svcRec: userRec = db.users.find_one( {"ConnectedServices.ID": svcRec["_id"]}) if userRec: return redirect("diagnostics_user", user=userRec["_id"]) if not userRec: return render(req, "diag/error_user_not_found.html") delta = True # Easier to set this to false in the one no-change case. if "sync" in req.POST: Sync.ScheduleImmediateSync(userRec, req.POST["sync"] == "Full") elif "unlock" in req.POST: db.users.update({"_id": ObjectId(user)}, {"$unset": { "SynchronizationWorker": None }}) elif "lock" in req.POST: db.users.update({"_id": ObjectId(user)}, {"$set": { "SynchronizationWorker": 1 }}) elif "hostrestrict" in req.POST: host = req.POST["host"] if host: db.users.update({"_id": ObjectId(user)}, {"$set": { "SynchronizationHostRestriction": host }}) else: db.users.update( {"_id": ObjectId(user)}, {"$unset": { "SynchronizationHostRestriction": None }}) elif "substitute" in req.POST: req.session["substituteUserid"] = user return redirect("dashboard") elif "svc_setauth" in req.POST and len(req.POST["authdetails"]): db.connections.update( {"_id": ObjectId(req.POST["id"])}, {"$set": { "Authorization": json.loads(req.POST["authdetails"]) }}) elif "svc_unlink" in req.POST: from tapiriik.services import Service from tapiriik.auth import User svcRec = Service.GetServiceRecordByID(req.POST["id"]) try: Service.DeleteServiceRecord(svcRec) except: pass try: User.DisconnectService(svcRec) except: pass elif "svc_marksync" in req.POST: db.connections.update( {"_id": ObjectId(req.POST["id"])}, {"$addToSet": { "SynchronizedActivities": req.POST["uid"] }}, multi=False) elif "svc_clearexc" in req.POST: db.connections.update({"_id": ObjectId(req.POST["id"])}, {"$unset": { "ExcludedActivities": 1 }}) elif "svc_clearacts" in req.POST: db.connections.update({"_id": ObjectId(req.POST["id"])}, {"$unset": { "SynchronizedActivities": 1 }}) Sync.SetNextSyncIsExhaustive(userRec, True) else: delta = False if delta: return redirect("diagnostics_user", user=user) return render(req, "diag/user.html", {"user": userRec})
def diag_user(req, user): try: userRec = db.users.find_one({"_id": ObjectId(user)}) except: userRec = None if not userRec: searchOpts = [{"Payments.Txn": user}, {"Payments.Email": user}] try: searchOpts.append({"AncestorAccounts": ObjectId(user)}) searchOpts.append({"ConnectedServices.ID": ObjectId(user)}) except: pass # Invalid format for ObjectId userRec = db.users.find_one({"$or": searchOpts}) if not userRec: searchOpts = [{"ExternalID": user}] try: searchOpts.append({"ExternalID": int(user)}) except: pass # Not an int svcRec = db.connections.find_one({"$or": searchOpts}) if svcRec: userRec = db.users.find_one( {"ConnectedServices.ID": svcRec["_id"]}) if userRec: return redirect("diagnostics_user", user=userRec["_id"]) if not userRec: return render(req, "diag/error_user_not_found.html") delta = True # Easier to set this to false in the one no-change case. if "sync" in req.POST: Sync.ScheduleImmediateSync(userRec, req.POST["sync"] == "Full") elif "unlock" in req.POST: db.users.update({"_id": ObjectId(user)}, {"$unset": { "SynchronizationWorker": None }}) elif "lock" in req.POST: db.users.update({"_id": ObjectId(user)}, {"$set": { "SynchronizationWorker": 1 }}) elif "requeue" in req.POST: db.users.update({"_id": ObjectId(user)}, {"$unset": { "QueuedAt": None }}) elif "hostrestrict" in req.POST: host = req.POST["host"] if host: db.users.update({"_id": ObjectId(user)}, {"$set": { "SynchronizationHostRestriction": host }}) else: db.users.update( {"_id": ObjectId(user)}, {"$unset": { "SynchronizationHostRestriction": None }}) elif "substitute" in req.POST: req.session["substituteUserid"] = user return redirect("dashboard") elif "svc_setauth" in req.POST and len(req.POST["authdetails"]): db.connections.update( {"_id": ObjectId(req.POST["id"])}, {"$set": { "Authorization": json.loads(req.POST["authdetails"]) }}) elif "svc_setconfig" in req.POST and len(req.POST["config"]): db.connections.update( {"_id": ObjectId(req.POST["id"])}, {"$set": { "Config": json.loads(req.POST["config"]) }}) elif "svc_unlink" in req.POST: from tapiriik.services import Service from tapiriik.auth import User svcRec = Service.GetServiceRecordByID(req.POST["id"]) try: Service.DeleteServiceRecord(svcRec) except: pass try: User.DisconnectService(svcRec) except: pass elif "svc_marksync" in req.POST: db.connections.update( {"_id": ObjectId(req.POST["id"])}, {"$addToSet": { "SynchronizedActivities": req.POST["uid"] }}, multi=False) elif "svc_clearexc" in req.POST: db.connections.update({"_id": ObjectId(req.POST["id"])}, {"$unset": { "ExcludedActivities": 1 }}) elif "svc_clearacts" in req.POST: db.connections.update({"_id": ObjectId(req.POST["id"])}, {"$unset": { "SynchronizedActivities": 1 }}) Sync.SetNextSyncIsExhaustive(userRec, True) elif "svc_toggle_poll_sub" in req.POST: from tapiriik.services import Service svcRec = Service.GetServiceRecordByID(req.POST["id"]) svcRec.SetPartialSyncTriggerSubscriptionState( not svcRec.PartialSyncTriggerSubscribed) elif "svc_toggle_poll_trigger" in req.POST: from tapiriik.services import Service svcRec = Service.GetServiceRecordByID(req.POST["id"]) db.connections.update({"_id": ObjectId(req.POST["id"])}, { "$set": { "TriggerPartialSync": not getattr(svcRec, "TriggerPartialSync", False) } }) elif "svc_tryagain" in req.POST: from tapiriik.services import Service svcRec = Service.GetServiceRecordByID(req.POST["id"]) db.connections.update({"_id": ObjectId(req.POST["id"])}, {"$pull": { "SyncErrors": { "Scope": "activity" } }}) act_recs = db.activity_records.find_one({"UserID": ObjectId(user)}) for act in act_recs["Activities"]: if "FailureCounts" in act and svcRec.Service.ID in act[ "FailureCounts"]: del act["FailureCounts"][svcRec.Service.ID] db.activity_records.save(act_recs) else: delta = False if delta: return redirect("diagnostics_user", user=user) return render(req, "diag/user.html", {"diag_user": userRec})
def ConnectService(user, serviceRecord): from tapiriik.services import Service, UserExceptionType existingUser = db.users.find_one({ "_id": { '$ne': ObjectId(user["_id"]) }, "ConnectedServices.ID": ObjectId(serviceRecord._id) }) if "ConnectedServices" not in user: user["ConnectedServices"] = [] delta = False if existingUser is not None: # merge merge merge user["ConnectedServices"] += existingUser["ConnectedServices"] if "Payments" in existingUser: if "Payments" not in user: user["Payments"] = [] user["Payments"] += existingUser["Payments"] if "FlowExceptions" in existingUser: if "FlowExceptions" not in user: user["FlowExceptions"] = [] user["FlowExceptions"] += existingUser["FlowExceptions"] user["Email"] = user["Email"] if "Email" in user and user[ "Email"] is not None else ( existingUser["Email"] if "Email" in existingUser else None) user["NonblockingSyncErrorCount"] = ( user["NonblockingSyncErrorCount"] if "NonblockingSyncErrorCount" in user and user["NonblockingSyncErrorCount"] is not None else 0 ) + (existingUser["NonblockingSyncErrorCount"] if "NonblockingSyncErrorCount" in existingUser and existingUser["NonblockingSyncErrorCount"] is not None else 0) user["BlockingSyncErrorCount"] = ( user["BlockingSyncErrorCount"] if "BlockingSyncErrorCount" in user and user["BlockingSyncErrorCount"] is not None else 0 ) + (existingUser["BlockingSyncErrorCount"] if "BlockingSyncErrorCount" in existingUser and existingUser["BlockingSyncErrorCount"] is not None else 0) user["SyncExclusionCount"] = ( user["SyncExclusionCount"] if "SyncExclusionCount" in user and user["SyncExclusionCount"] is not None else 0) + ( existingUser["SyncExclusionCount"] if "SyncExclusionCount" in existingUser and existingUser["SyncExclusionCount"] is not None else 0) user[ "Created"] = user["Created"] if user["Created"] < existingUser[ "Created"] else existingUser["Created"] if "AncestorAccounts" not in user: user["AncestorAccounts"] = [] user["AncestorAccounts"] += existingUser["AncestorAccounts"] if "AncestorAccounts" in existingUser else [] user["AncestorAccounts"] += [existingUser["_id"]] user["Timezone"] = user["Timezone"] if user[ "Timezone"] else existingUser["Timezone"] delta = True db.users.remove({"_id": existingUser["_id"]}) else: if serviceRecord._id not in [ x["ID"] for x in user["ConnectedServices"] ]: # we might be connecting a second account for the same service for duplicateConn in [ x for x in user["ConnectedServices"] if x["Service"] == serviceRecord.Service.ID ]: dupeRecord = User.GetConnectionRecord( user, serviceRecord.Service.ID ) # this'll just pick the first connection of type, but we repeat the right # of times anyways Service.DeleteServiceRecord(dupeRecord) # We used to call DisconnectService() here, but the results of that call were getting overwritten, which was unfortunate. user["ConnectedServices"] = [ x for x in user["ConnectedServices"] if x["Service"] != serviceRecord.Service.ID ] user["ConnectedServices"].append({ "Service": serviceRecord.Service.ID, "ID": serviceRecord._id }) delta = True db.users.update({"_id": user["_id"]}, user) if delta or ( hasattr(serviceRecord, "SyncErrors") and len(serviceRecord.SyncErrors) > 0 ): # also schedule an immediate sync if there is an outstanding error (i.e. user reconnected) db.connections.update( {"_id": serviceRecord._id}, { "$pull": { "SyncErrors": { "UserException.Type": UserExceptionType.Authorization } } } ) # Pull all auth-related errors from the service so they don't continue to see them while the sync completes. Sync.SetNextSyncIsExhaustive( user, True ) # exhaustive, so it'll pick up activities from newly added services / ones lost during an error if hasattr(serviceRecord, "SyncErrors") and len(serviceRecord.SyncErrors) > 0: Sync.ScheduleImmediateSync(user)
def diag_user(req, user): userRec = db.users.find_one({"_id": ObjectId(user)}) if not userRec: userRec = db.users.find_one({"AncestorAccounts": ObjectId(user)}) if userRec: return redirect("diagnostics_user", user=userRec["_id"]) if not userRec: return render(req, "diag/error_user_not_found.html") delta = False if "sync" in req.POST: Sync.ScheduleImmediateSync(userRec, req.POST["sync"] == "Full") delta = True elif "unlock" in req.POST: db.users.update({"_id": ObjectId(user)}, {"$unset": { "SynchronizationWorker": None }}) delta = True elif "lock" in req.POST: db.users.update({"_id": ObjectId(user)}, {"$set": { "SynchronizationWorker": 1 }}) delta = True elif "substitute" in req.POST: req.session["substituteUserid"] = user return redirect("dashboard") elif "svc_setauth" in req.POST and len(req.POST["authdetails"]): db.connections.update( {"_id": ObjectId(req.POST["id"])}, {"$set": { "Authorization": json.loads(req.POST["authdetails"]) }}) delta = True elif "svc_unlink" in req.POST: from tapiriik.services import Service from tapiriik.auth import User svcRec = Service.GetServiceRecordByID(req.POST["id"]) try: Service.DeleteServiceRecord(svcRec) except: pass try: User.DisconnectService(svcRec) except: pass delta = True elif "svc_marksync" in req.POST: from tapiriik.services import Service from tapiriik.auth import User db.connections.update( {"_id": ObjectId(req.POST["id"])}, {"$addToSet": { "SynchronizedActivities": req.POST["uid"] }}, multi=False) delta = True elif "svc_clearexc" in req.POST: from tapiriik.services import Service from tapiriik.auth import User db.connections.update({"_id": ObjectId(req.POST["id"])}, {"$unset": { "ExcludedActivities": 1 }}) delta = True elif "svc_clearacts" in req.POST: from tapiriik.services import Service from tapiriik.auth import User db.connections.update({"_id": ObjectId(req.POST["id"])}, {"$unset": { "SynchronizedActivities": 1 }}) Sync.SetNextSyncIsExhaustive(userRec, True) delta = True if delta: return redirect("diagnostics_user", user=user) return render(req, "diag/user.html", {"user": userRec})