<commit_before>import datetime
import json
import logging
import platform
import random

import requests
from django.core.management import call_command
from django.core.urlresolvers import reverse
from morango.models import InstanceIDModel
from rest_framework import status

import kolibri
from kolibri.core.auth.models import FacilityUser
from kolibri.core.device.models import UserSyncStatus
from kolibri.core.device.utils import DeviceNotProvisioned
from kolibri.core.device.utils import get_device_setting
from kolibri.core.public.constants.user_sync_statuses import QUEUED
from kolibri.core.public.constants.user_sync_statuses import SYNC
from kolibri.core.tasks.api import prepare_soud_sync_job
from kolibri.core.tasks.api import prepare_sync_task
from kolibri.core.tasks.job import Job
from kolibri.core.tasks.main import queue
from kolibri.core.tasks.main import scheduler


logger = logging.getLogger(__name__)


device_info_keys = {
    "1": [
        "application",
        "kolibri_version",
        "instance_id",
        "device_name",
        "operating_system",
    ],
    "2": [
        "application",
        "kolibri_version",
        "instance_id",
        "device_name",
        "operating_system",
        "subset_of_users_device",
    ],
}

DEVICE_INFO_VERSION = "2"


def get_device_info(version=DEVICE_INFO_VERSION):
    """
    Returns metadata information about the device
    The default kwarg version should always be the latest
    version of device info that this function supports.
    We maintain historic versions for backwards compatibility
    """

    if version not in device_info_keys:
        version = DEVICE_INFO_VERSION

    instance_model = InstanceIDModel.get_or_create_current_instance()[0]
    try:
        device_name = get_device_setting("name")
        subset_of_users_device = get_device_setting("subset_of_users_device")
    # When Koliri starts at the first time, and device hasn't been created
    except DeviceNotProvisioned:
        device_name = instance_model.hostname
        subset_of_users_device = False

    all_info = {
        "application": "kolibri",
        "kolibri_version": kolibri.__version__,
        "instance_id": instance_model.id,
        "device_name": device_name,
        "operating_system": platform.system(),
        "subset_of_users_device": subset_of_users_device,
    }

    info = {}

    # By this point, we have validated that the version is in device_info_keys
    for key in device_info_keys.get(version, []):
        info[key] = all_info[key]

    return info


def startpeerusersync(server, user_id):
    """
    Initiate a SYNC (PULL + PUSH) of a specific user from another device.
    """

    user = FacilityUser.objects.get(pk=user_id)
    facility_id = user.facility.id

    device_info = get_device_info()

    extra_metadata = prepare_sync_task(
        facility_id,
        user_id,
        user.username,
        user.facility.name,
        device_info["device_name"],
        device_info["instance_id"],
        server,
        type="SYNCPEER/SINGLE",
    )

    job_data = prepare_soud_sync_job(
        server, facility_id, user_id, extra_metadata=extra_metadata
    )

    job_id = queue.enqueue(call_command, "sync", **job_data)

    return job_id


def begin_request_soud_sync(server, user):
    """
    Enqueue a task to request this SoUD to be
    synced with a server
    """
    info = get_device_info()
    if not info["subset_of_users_device"]:
        # this does not make sense unless this is a SoUD
        logger.warn("Only Subsets of Users Devices can do this")
        return
    logger.info("Queuing SoUD syncing request")
    queue.enqueue(request_soud_sync, server, user)


def request_soud_sync(server, user=None, queue_id=None, ttl=10):
    """
    Make a request to the serverurl endpoint to sync this SoUD (Subset of Users Device)<commit_msg>do not enqueue a new sync if there's one in progress<commit_after>import datetime
import json
import logging
import platform
import random

import requests
from django.core.management import call_command
from django.core.urlresolvers import reverse
from morango.models import InstanceIDModel
from rest_framework import status

import kolibri
from kolibri.core.auth.models import FacilityUser
from kolibri.core.device.models import UserSyncStatus
from kolibri.core.device.utils import DeviceNotProvisioned
from kolibri.core.device.utils import get_device_setting
from kolibri.core.public.constants.user_sync_statuses import QUEUED
from kolibri.core.public.constants.user_sync_statuses import SYNC
from kolibri.core.tasks.api import prepare_soud_sync_job
from kolibri.core.tasks.api import prepare_sync_task
from kolibri.core.tasks.job import Job
from kolibri.core.tasks.job import State
from kolibri.core.tasks.main import queue
from kolibri.core.tasks.main import scheduler


logger = logging.getLogger(__name__)


device_info_keys = {
    "1": [
        "application",
        "kolibri_version",
        "instance_id",
        "device_name",
        "operating_system",
    ],
    "2": [
        "application",
        "kolibri_version",
        "instance_id",
        "device_name",
        "operating_system",
        "subset_of_users_device",
    ],
}

DEVICE_INFO_VERSION = "2"


def get_device_info(version=DEVICE_INFO_VERSION):
    """
    Returns metadata information about the device
    The default kwarg version should always be the latest
    version of device info that this function supports.
    We maintain historic versions for backwards compatibility
    """

    if version not in device_info_keys:
        version = DEVICE_INFO_VERSION

    instance_model = InstanceIDModel.get_or_create_current_instance()[0]
    try:
        device_name = get_device_setting("name")
        subset_of_users_device = get_device_setting("subset_of_users_device")
    # When Koliri starts at the first time, and device hasn't been created
    except DeviceNotProvisioned:
        device_name = instance_model.hostname
        subset_of_users_device = False

    all_info = {
        "application": "kolibri",
        "kolibri_version": kolibri.__version__,
        "instance_id": instance_model.id,
        "device_name": device_name,
        "operating_system": platform.system(),
        "subset_of_users_device": subset_of_users_device,
    }

    info = {}

    # By this point, we have validated that the version is in device_info_keys
    for key in device_info_keys.get(version, []):
        info[key] = all_info[key]

    return info


def startpeerusersync(server, user_id):
    """
    Initiate a SYNC (PULL + PUSH) of a specific user from another device.
    """

    user = FacilityUser.objects.get(pk=user_id)
    facility_id = user.facility.id

    device_info = get_device_info()

    extra_metadata = prepare_sync_task(
        facility_id,
        user_id,
        user.username,
        user.facility.name,
        device_info["device_name"],
        device_info["instance_id"],
        server,
        type="SYNCPEER/SINGLE",
    )

    job_data = prepare_soud_sync_job(
        server, facility_id, user_id, extra_metadata=extra_metadata
    )

    job_id = queue.enqueue(call_command, "sync", **job_data)

    return job_id


def begin_request_soud_sync(server, user):
    """
    Enqueue a task to request this SoUD to be
    synced with a server
    """
    info = get_device_info()
    if not info["subset_of_users_device"]:
        # this does not make sense unless this is a SoUD
        logger.warn("Only Subsets of Users Devices can do this")
        return
    if UserSyncStatus.objects.get(user_id=user).queued:
        failed_jobs = [
            j
            for j in queue.jobs
            if j.state == State.FAILED
            and j.extra_metadata.get("started_by", None) == user
            and j.extra_metadata.get("type", None) == "SYNCPEER/SINGLE"
        ]
        if failed_jobs:
            for j in failed_jobs:
                queue.clear_job(j.job_id)
            # if previous sync jobs have failed, unblock UserSyncStatus to try again:
            UserSyncStatus.objects.update_or_create(
                user_id=user, defaults={"queued": False}
            )
        else:
            return  # If there are pending and not failed jobs, don't enqueue a new one
    logger.info("Queuing SoUD syncing request for user {}".format(user))
    queue.enqueue(request_soud_sync, server, user)


def request_soud_sync(server, user=None, queue_id=None, ttl=10):
    """
    Make a request to the serverurl endpoint to sync this SoUD (Subset of Users Device)
{'fraction_non_alphanumeric': 0.07890743550834597, 'fraction_numerical': 0.0014007237072487452, 'mean_word_length': 3.1734047735021917, 'pattern_counts': {'":': 17, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4304966', 'n_tokens_mistral': 2582, 'n_tokens_neox': 2550, 'n_words': 672}