mirror of
https://github.com/GAM-team/GAM.git
synced 2026-06-05 14:51:39 +00:00
Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d46dd46732 | ||
|
|
8eb72ae6e7 | ||
|
|
6a421d3b78 | ||
|
|
f71a14126e | ||
|
|
35c2024eec |
@@ -5,7 +5,7 @@ default_language_version:
|
||||
|
||||
repos:
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: v2.4.0
|
||||
rev: v2.5.0
|
||||
hooks:
|
||||
- id: trailing-whitespace
|
||||
- id: end-of-file-fixer
|
||||
@@ -17,13 +17,13 @@ repos:
|
||||
- id: check-merge-conflict
|
||||
|
||||
- repo: https://github.com/pre-commit/mirrors-yapf
|
||||
rev: v0.29.0
|
||||
rev: v0.30.0
|
||||
hooks:
|
||||
- id: yapf
|
||||
args: [--style=google, --in-place]
|
||||
|
||||
- repo: https://github.com/PyCQA/pylint
|
||||
rev: pylint-2.4.4
|
||||
rev: pylint-2.5.0
|
||||
hooks:
|
||||
- id: pylint
|
||||
args: [--output-format=colorized]
|
||||
|
||||
@@ -602,7 +602,7 @@ def doGAMCheckForUpdates(forceCheck=False):
|
||||
controlflow.system_error_exit(
|
||||
4, 'GAM Latest Version information not available')
|
||||
|
||||
current_version = gam_version
|
||||
current_version = GAM_VERSION
|
||||
now_time = int(time.time())
|
||||
if forceCheck:
|
||||
check_url = GAM_ALL_RELEASES # includes pre-releases
|
||||
@@ -709,15 +709,15 @@ def doGAMVersion(checkForArgs=True):
|
||||
else:
|
||||
controlflow.invalid_argument_exit(sys.argv[i], 'gam version')
|
||||
if simple:
|
||||
sys.stdout.write(gam_version)
|
||||
sys.stdout.write(GAM_VERSION)
|
||||
return
|
||||
pyversion = platform.python_version()
|
||||
cpu_bits = struct.calcsize('P') * 8
|
||||
api_client_ver = pkg_resources.get_distribution(
|
||||
'google-api-python-client').version
|
||||
print(
|
||||
(f'GAM {gam_version} - {GAM_URL} - {GM_Globals[GM_GAM_TYPE]}\n'
|
||||
f'{gam_author}\n'
|
||||
(f'GAM {GAM_VERSION} - {GAM_URL} - {GM_Globals[GM_GAM_TYPE]}\n'
|
||||
f'{GAM_AUTHOR}\n'
|
||||
f'Python {pyversion} {cpu_bits}-bit {sys.version_info.releaselevel}\n'
|
||||
f'google-api-python-client {api_client_ver}\n'
|
||||
f'{getOSPlatform()} {platform.machine()}\n'
|
||||
@@ -3296,7 +3296,7 @@ def doPrinterRegister():
|
||||
'uuid':
|
||||
_getValueFromOAuth('sub'),
|
||||
'manufacturer':
|
||||
gam_author,
|
||||
GAM_AUTHOR,
|
||||
'model':
|
||||
'cp1',
|
||||
'gcp_version':
|
||||
@@ -3308,7 +3308,7 @@ def doPrinterRegister():
|
||||
'update_url':
|
||||
GAM_RELEASES,
|
||||
'firmware':
|
||||
gam_version,
|
||||
GAM_VERSION,
|
||||
'semantic_state': {
|
||||
'version': '1.0',
|
||||
'printer': {
|
||||
@@ -8609,21 +8609,24 @@ def doCreateOrRotateServiceAccountKeys(iam=None,
|
||||
name, local_key_size)
|
||||
print(' Uploading new public certificate to Google...')
|
||||
max_retries = 10
|
||||
for i in range(1, max_retries+1):
|
||||
try:
|
||||
result = gapi.call(iam.projects().serviceAccounts().keys(),
|
||||
'upload',
|
||||
throw_reasons=[gapi_errors.ErrorReason.NOT_FOUND],
|
||||
name=name,
|
||||
body={'publicKeyData': publicKeyData})
|
||||
break
|
||||
except gapi_errors.GapiNotFoundError as e:
|
||||
if i == max_retries:
|
||||
raise e
|
||||
sleep_time = i*5
|
||||
if i > 3:
|
||||
print(f'Waiting for Service Account creation to complete. Sleeping {sleep_time} seconds\n')
|
||||
time.sleep(sleep_time)
|
||||
for i in range(1, max_retries + 1):
|
||||
try:
|
||||
result = gapi.call(
|
||||
iam.projects().serviceAccounts().keys(),
|
||||
'upload',
|
||||
throw_reasons=[gapi_errors.ErrorReason.NOT_FOUND],
|
||||
name=name,
|
||||
body={'publicKeyData': publicKeyData})
|
||||
break
|
||||
except gapi_errors.GapiNotFoundError as e:
|
||||
if i == max_retries:
|
||||
raise e
|
||||
sleep_time = i * 5
|
||||
if i > 3:
|
||||
print(
|
||||
f'Waiting for Service Account creation to complete. Sleeping {sleep_time} seconds\n'
|
||||
)
|
||||
time.sleep(sleep_time)
|
||||
private_key_id = result['name'].rsplit('/', 1)[-1]
|
||||
oauth2service_data = _formatOAuth2ServiceData(project_id, client_email,
|
||||
client_id, private_key,
|
||||
@@ -9894,8 +9897,8 @@ def doWhatIs():
|
||||
],
|
||||
userKey=email,
|
||||
fields='id,primaryEmail')
|
||||
if (user_or_alias['primaryEmail'].lower() == email) or (
|
||||
user_or_alias['id'] == email):
|
||||
if (user_or_alias['primaryEmail'].lower()
|
||||
== email) or (user_or_alias['id'] == email):
|
||||
sys.stderr.write(f'{email} is a user\n\n')
|
||||
doGetUserInfo(user_email=email)
|
||||
return
|
||||
@@ -12915,9 +12918,8 @@ def getUsersToModify(entity_type=None,
|
||||
query=query)
|
||||
for member in members:
|
||||
email = member['primaryEmail']
|
||||
if (checkSuspended is None or
|
||||
checkSuspended == member['suspended']
|
||||
) and email not in usersSet:
|
||||
if (checkSuspended is None or checkSuspended
|
||||
== member['suspended']) and email not in usersSet:
|
||||
usersSet.add(email)
|
||||
users.append(email)
|
||||
if not silent:
|
||||
|
||||
@@ -328,6 +328,10 @@ def get_gapi_error_detail(e,
|
||||
message = error['error']['errors'][0]['message']
|
||||
except KeyError:
|
||||
message = error['error']['message']
|
||||
if http_status == 404:
|
||||
if 'Requested entity was not found' in message or 'does not exist' in message:
|
||||
error = _create_http_error_dict(404, ErrorReason.NOT_FOUND.value,
|
||||
message)
|
||||
else:
|
||||
if 'error_description' in error:
|
||||
if error['error_description'] == 'Invalid Value':
|
||||
|
||||
@@ -306,7 +306,9 @@ def showReport():
|
||||
elif myarg == 'fulldatarequired':
|
||||
fullDataRequired = []
|
||||
fdr = sys.argv[i + 1].lower()
|
||||
if fdr and fdr != 'all':
|
||||
if fdr and fdr == 'all':
|
||||
fullDataRequired = 'all'
|
||||
else:
|
||||
fullDataRequired = fdr.replace(',', ' ').split()
|
||||
i += 2
|
||||
elif myarg == 'start':
|
||||
@@ -338,23 +340,24 @@ def showReport():
|
||||
if report == 'user':
|
||||
while True:
|
||||
try:
|
||||
if fullDataRequired is not None:
|
||||
warnings = gapi.get_items(rep.userUsageReport(),
|
||||
'get',
|
||||
'warnings',
|
||||
throw_reasons=throw_reasons,
|
||||
date=tryDate,
|
||||
userKey=userKey,
|
||||
customerId=customerId,
|
||||
orgUnitID=orgUnitId,
|
||||
fields='warnings')
|
||||
fullData, tryDate = _check_full_data_available(
|
||||
warnings, tryDate, fullDataRequired)
|
||||
if fullData < 0:
|
||||
print('No user report available.')
|
||||
sys.exit(1)
|
||||
if fullData == 0:
|
||||
continue
|
||||
one_page = gapi.call(rep.userUsageReport(),
|
||||
'get',
|
||||
throw_reasons=throw_reasons,
|
||||
date=tryDate,
|
||||
userKey=userKey,
|
||||
customerId=customerId,
|
||||
orgUnitID=orgUnitId,
|
||||
fields='warnings,usageReports',
|
||||
maxResults=1)
|
||||
warnings = one_page.get('warnings', [])
|
||||
has_reports = bool(one_page.get('usageReports'))
|
||||
fullData, tryDate = _check_full_data_available(
|
||||
warnings, tryDate, fullDataRequired, has_reports)
|
||||
if fullData < 0:
|
||||
print('No user report available.')
|
||||
sys.exit(1)
|
||||
if fullData == 0:
|
||||
continue
|
||||
page_message = gapi.got_total_items_msg('Users', '...\n')
|
||||
usage = gapi.get_all_pages(rep.userUsageReport(),
|
||||
'get',
|
||||
@@ -397,21 +400,21 @@ def showReport():
|
||||
elif report == 'customer':
|
||||
while True:
|
||||
try:
|
||||
if fullDataRequired is not None:
|
||||
warnings = gapi.get_items(rep.customerUsageReports(),
|
||||
'get',
|
||||
'warnings',
|
||||
throw_reasons=throw_reasons,
|
||||
customerId=customerId,
|
||||
date=tryDate,
|
||||
fields='warnings')
|
||||
fullData, tryDate = _check_full_data_available(
|
||||
warnings, tryDate, fullDataRequired)
|
||||
if fullData < 0:
|
||||
print('No customer report available.')
|
||||
sys.exit(1)
|
||||
if fullData == 0:
|
||||
continue
|
||||
first_page = gapi.call(rep.customerUsageReports(),
|
||||
'get',
|
||||
throw_reasons=throw_reasons,
|
||||
customerId=customerId,
|
||||
date=tryDate,
|
||||
fields='warnings,usageReports')
|
||||
warnings = first_page.get('warnings', [])
|
||||
has_reports = bool(first_page.get('usageReports'))
|
||||
fullData, tryDate = _check_full_data_available(
|
||||
warnings, tryDate, fullDataRequired, has_reports)
|
||||
if fullData < 0:
|
||||
print('No customer report available.')
|
||||
sys.exit(1)
|
||||
if fullData == 0:
|
||||
continue
|
||||
usage = gapi.get_all_pages(rep.customerUsageReports(),
|
||||
'get',
|
||||
'usageReports',
|
||||
@@ -558,16 +561,21 @@ def _adjust_date(errMsg):
|
||||
return str(match_date.group(1))
|
||||
|
||||
|
||||
def _check_full_data_available(warnings, tryDate, fullDataRequired):
|
||||
def _check_full_data_available(warnings, tryDate, fullDataRequired,
|
||||
has_reports):
|
||||
one_day = datetime.timedelta(days=1)
|
||||
tryDateTime = datetime.datetime.strptime(tryDate, YYYYMMDD_FORMAT)
|
||||
# move to day before if we don't have at least one usageReport
|
||||
if not has_reports:
|
||||
tryDateTime -= one_day
|
||||
return (0, tryDateTime.strftime(YYYYMMDD_FORMAT))
|
||||
for warning in warnings:
|
||||
if warning['code'] == 'PARTIAL_DATA_AVAILABLE':
|
||||
for app in warning['data']:
|
||||
if app['key'] == 'application' and \
|
||||
app['value'] != 'docs' and \
|
||||
(not fullDataRequired or app['value'] in fullDataRequired):
|
||||
tryDateTime = datetime.datetime.strptime(
|
||||
tryDate, YYYYMMDD_FORMAT)
|
||||
fullDataRequired is not None and \
|
||||
(fullDataRequired == 'all' or app['value'] in fullDataRequired):
|
||||
tryDateTime -= one_day
|
||||
return (0, tryDateTime.strftime(YYYYMMDD_FORMAT))
|
||||
elif warning['code'] == 'DATA_NOT_AVAILABLE':
|
||||
|
||||
153
src/gam/var.py
153
src/gam/var.py
@@ -1,3 +1,4 @@
|
||||
"""Variables common across modules"""
|
||||
import os
|
||||
import ssl
|
||||
import string
|
||||
@@ -5,13 +6,13 @@ import sys
|
||||
import platform
|
||||
import re
|
||||
|
||||
gam_author = 'Jay Lee <jay0lee@gmail.com>'
|
||||
gam_version = '5.08'
|
||||
gam_license = 'Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0)'
|
||||
GAM_AUTHOR = 'Jay Lee <jay0lee@gmail.com>'
|
||||
GAM_VERSION = '5.09'
|
||||
GAM_LICENSE = 'Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0)'
|
||||
|
||||
GAM_URL = 'https://git.io/gam'
|
||||
GAM_INFO = (
|
||||
f'GAM {gam_version} - {GAM_URL} / {gam_author} / '
|
||||
f'GAM {GAM_VERSION} - {GAM_URL} / {GAM_AUTHOR} / '
|
||||
f'Python {platform.python_version()} {sys.version_info.releaselevel} / '
|
||||
f'{platform.platform()} {platform.machine()}')
|
||||
|
||||
@@ -930,7 +931,12 @@ CROS_DISK_VOLUME_REPORTS_ARGUMENTS = [
|
||||
CROS_SYSTEM_RAM_FREE_REPORTS_ARGUMENTS = [
|
||||
'systemramfreereports',
|
||||
]
|
||||
CROS_LISTS_ARGUMENTS = CROS_ACTIVE_TIME_RANGES_ARGUMENTS + CROS_RECENT_USERS_ARGUMENTS + CROS_DEVICE_FILES_ARGUMENTS + CROS_CPU_STATUS_REPORTS_ARGUMENTS + CROS_DISK_VOLUME_REPORTS_ARGUMENTS + CROS_SYSTEM_RAM_FREE_REPORTS_ARGUMENTS
|
||||
CROS_LISTS_ARGUMENTS = CROS_ACTIVE_TIME_RANGES_ARGUMENTS + \
|
||||
CROS_RECENT_USERS_ARGUMENTS + \
|
||||
CROS_DEVICE_FILES_ARGUMENTS + \
|
||||
CROS_CPU_STATUS_REPORTS_ARGUMENTS + \
|
||||
CROS_DISK_VOLUME_REPORTS_ARGUMENTS + \
|
||||
CROS_SYSTEM_RAM_FREE_REPORTS_ARGUMENTS
|
||||
CROS_START_ARGUMENTS = ['start', 'startdate', 'oldestdate']
|
||||
CROS_END_ARGUMENTS = ['end', 'enddate']
|
||||
|
||||
@@ -1097,7 +1103,8 @@ GM_Globals = {
|
||||
#
|
||||
# Global variables defined by environment variables/signal files
|
||||
#
|
||||
# Automatically generate gam batch command if number of users specified in gam users xxx command exceeds this number
|
||||
# Automatically generate gam batch command if number of users specified in gam
|
||||
# users xxx command exceeds this number
|
||||
# Default: 0, don't automatically generate gam batch commands
|
||||
GC_AUTO_BATCH_MIN = 'auto_batch_min'
|
||||
# When processing items in batches, how many should be processed in each batch
|
||||
@@ -1110,20 +1117,24 @@ GC_CACHE_DISCOVERY_ONLY = 'cache_discovery_only'
|
||||
GC_CHARSET = 'charset'
|
||||
# Path to client_secrets.json
|
||||
GC_CLIENT_SECRETS_JSON = 'client_secrets_json'
|
||||
# GAM config directory containing client_secrets.json, oauth2.txt, oauth2service.json, extra_args.txt
|
||||
# GAM config directory containing client_secrets.json, oauth2.txt,
|
||||
# oauth2service.json, extra_args.txt
|
||||
GC_CONFIG_DIR = 'config_dir'
|
||||
# custmerId from gam.cfg or retrieved from Google
|
||||
GC_CUSTOMER_ID = 'customer_id'
|
||||
# If debug_level > 0: extra_args[u'prettyPrint'] = True, httplib2.debuglevel = gam_debug_level, appsObj.debug = True
|
||||
# If debug_level > 0: extra_args[u'prettyPrint'] = True,
|
||||
# httplib2.debuglevel = gam_debug_level, appsObj.debug = True
|
||||
GC_DEBUG_LEVEL = 'debug_level'
|
||||
# ID Token decoded from OAuth 2.0 refresh token response. Includes hd (domain) and email of authorized user
|
||||
# ID Token decoded from OAuth 2.0 refresh token response. Includes hd (domain)
|
||||
# and email of authorized user
|
||||
GC_DECODED_ID_TOKEN = 'decoded_id_token'
|
||||
# Domain obtained from gam.cfg or oauth2.txt
|
||||
GC_DOMAIN = 'domain'
|
||||
# Google Drive download directory
|
||||
GC_DRIVE_DIR = 'drive_dir'
|
||||
# If no_browser is False, writeCSVfile won't open a browser when todrive is set
|
||||
# and doRequestOAuth prints a link and waits for the verification code when oauth2.txt is being created
|
||||
# and doRequestOAuth prints a link and waits for the verification code when
|
||||
# oauth2.txt is being created
|
||||
GC_NO_BROWSER = 'no_browser'
|
||||
# oauth_browser forces usage of web server OAuth flow that proved problematic.
|
||||
GC_OAUTH_BROWSER = 'oauth_browser'
|
||||
@@ -1158,7 +1169,7 @@ GC_TLS_MAX_VERSION = 'tls_max_ver'
|
||||
# Path to certificate authority file for validating TLS hosts
|
||||
GC_CA_FILE = 'ca_file'
|
||||
|
||||
tls_min = 'TLSv1_2' if hasattr(ssl.SSLContext(), 'minimum_version') else None
|
||||
TLS_MIN = 'TLSv1_2' if hasattr(ssl.SSLContext(), 'minimum_version') else None
|
||||
GC_Defaults = {
|
||||
GC_AUTO_BATCH_MIN: 0,
|
||||
GC_BATCH_SIZE: 50,
|
||||
@@ -1186,7 +1197,7 @@ GC_Defaults = {
|
||||
GC_CSV_HEADER_FILTER: '',
|
||||
GC_CSV_HEADER_DROP_FILTER: '',
|
||||
GC_CSV_ROW_FILTER: '',
|
||||
GC_TLS_MIN_VERSION: tls_min,
|
||||
GC_TLS_MIN_VERSION: TLS_MIN,
|
||||
GC_TLS_MAX_VERSION: None,
|
||||
GC_CA_FILE: None,
|
||||
}
|
||||
@@ -1321,20 +1332,52 @@ CLEAR_NONE_ARGUMENT = [
|
||||
'none',
|
||||
]
|
||||
#
|
||||
MESSAGE_API_ACCESS_CONFIG = 'API access is configured in your Control Panel under: Security-Show more-Advanced settings-Manage API client access'
|
||||
MESSAGE_API_ACCESS_DENIED = 'API access Denied.\n\nPlease make sure the Client ID: {0} is authorized for the API Scope(s): {1}'
|
||||
MESSAGE_GAM_EXITING_FOR_UPDATE = 'GAM is now exiting so that you can overwrite this old version with the latest release'
|
||||
MESSAGE_GAM_OUT_OF_MEMORY = 'GAM has run out of memory. If this is a large G Suite instance, you should use a 64-bit version of GAM on Windows or a 64-bit version of Python on other systems.'
|
||||
MESSAGE_HEADER_NOT_FOUND_IN_CSV_HEADERS = 'Header "{0}" not found in CSV headers of "{1}".'
|
||||
MESSAGE_HIT_CONTROL_C_TO_UPDATE = '\n\nHit CTRL+C to visit the GAM website and download the latest release or wait 15 seconds continue with this boring old version. GAM won\'t bother you with this announcement for 1 week or you can create a file named noupdatecheck.txt in the same location as gam.py or gam.exe and GAM won\'t ever check for updates.'
|
||||
MESSAGE_API_ACCESS_CONFIG = 'API access is configured in your Control Panel' \
|
||||
' under: Security-Show more-Advanced' \
|
||||
' settings-Manage API client access'
|
||||
MESSAGE_API_ACCESS_DENIED = 'API access Denied.\n\nPlease make sure the Client' \
|
||||
' ID: {0} is authorized for the API Scope(s): {1}'
|
||||
MESSAGE_GAM_EXITING_FOR_UPDATE = 'GAM is now exiting so that you can' \
|
||||
' overwrite this old version with the' \
|
||||
' latest release'
|
||||
MESSAGE_GAM_OUT_OF_MEMORY = 'GAM has run out of memory. If this is a large' \
|
||||
' G Suite instance, you should use a 64-bit' \
|
||||
' version of GAM on Windows or a 64-bit version' \
|
||||
' of Python on other systems.'
|
||||
MESSAGE_HEADER_NOT_FOUND_IN_CSV_HEADERS = 'Header "{0}" not found in CSV' \
|
||||
' headers of "{1}".'
|
||||
MESSAGE_HIT_CONTROL_C_TO_UPDATE = '\n\nHit CTRL+C to visit the GAM website' \
|
||||
' and download the latest release or wait' \
|
||||
' 15 seconds continue with this boring old' \
|
||||
' version. GAM won\'t bother you with this ' \
|
||||
' announcement for 1 week or you can create' \
|
||||
' a file named noupdatecheck.txt in the same' \
|
||||
' location as gam.py or gam.exe and GAM' \
|
||||
' won\'t ever check for updates.'
|
||||
MESSAGE_INVALID_JSON = 'The file {0} has an invalid format.'
|
||||
MESSAGE_NO_DISCOVERY_INFORMATION = 'No online discovery doc and {0} does not exist locally'
|
||||
MESSAGE_NO_TRANSFER_LACK_OF_DISK_SPACE = 'Cowardly refusing to perform migration due to lack of target drive space. Source size: {0}mb Target Free: {1}mb'
|
||||
MESSAGE_RESULTS_TOO_LARGE_FOR_GOOGLE_SPREADSHEET = 'Results are too large for Google Spreadsheets. Uploading as a regular CSV file.'
|
||||
MESSAGE_SERVICE_NOT_APPLICABLE = 'Service not applicable for this address: {0}. Please make sure service is enabled for user and run\n\ngam user <user> check serviceaccount\n\nfor further instructions'
|
||||
MESSAGE_INSTRUCTIONS_OAUTH2SERVICE_JSON = 'Please run\n\ngam create project\ngam user <user> check serviceaccount\n\nto create and configure a service account.'
|
||||
MESSAGE_UPDATE_GAM_TO_64BIT = "You're running a 32-bit version of GAM on a 64-bit version of Windows, upgrade to a windows-x86_64 version of GAM"
|
||||
MESSAGE_YOUR_SYSTEM_TIME_DIFFERS_FROM_GOOGLE_BY = 'Your system time differs from %s by %s'
|
||||
MESSAGE_NO_DISCOVERY_INFORMATION = 'No online discovery doc and {0} does not' \
|
||||
' exist locally'
|
||||
MESSAGE_NO_TRANSFER_LACK_OF_DISK_SPACE = 'Cowardly refusing to perform' \
|
||||
' migration due to lack of target' \
|
||||
' drive space. Source size: {0}mb' \
|
||||
' Target Free: {1}mb'
|
||||
MESSAGE_RESULTS_TOO_LARGE_FOR_GOOGLE_SPREADSHEET = 'Results are too large for' \
|
||||
' Google Spreadsheets.' \
|
||||
' Uploading as a regular' \
|
||||
' CSV file.'
|
||||
MESSAGE_SERVICE_NOT_APPLICABLE = 'Service not applicable for this address:' \
|
||||
' {0}. Please make sure service is enabled' \
|
||||
' for user and run\n\ngam user <user> check' \
|
||||
' serviceaccount\n\nfor further instructions'
|
||||
MESSAGE_INSTRUCTIONS_OAUTH2SERVICE_JSON = 'Please run\n\ngam create project\n' \
|
||||
'gam user <user> check ' \
|
||||
'serviceaccount\n\nto create and' \
|
||||
' configure a service account.'
|
||||
MESSAGE_UPDATE_GAM_TO_64BIT = 'You\'re running a 32-bit version of GAM on a' \
|
||||
' 64-bit version of Windows, upgrade to a' \
|
||||
' windows-x86_64 version of GAM'
|
||||
MESSAGE_YOUR_SYSTEM_TIME_DIFFERS_FROM_GOOGLE_BY = 'Your system time differs' \
|
||||
' from %s by %s'
|
||||
|
||||
USER_ADDRESS_TYPES = ['home', 'work', 'other']
|
||||
USER_EMAIL_TYPES = ['home', 'work', 'other']
|
||||
@@ -1587,167 +1630,151 @@ LANGUAGE_CODES_MAP = {
|
||||
'ak': 'ak',
|
||||
'am': 'am',
|
||||
'ar': 'ar',
|
||||
'az': 'az', #Luo, Afrikaans, Irish, Akan, Amharic, Arabica, Azerbaijani
|
||||
'az': 'az',
|
||||
'be': 'be',
|
||||
'bem': 'bem',
|
||||
'bg': 'bg',
|
||||
'bn': 'bn',
|
||||
'br': 'br',
|
||||
'bs': 'bs',
|
||||
'ca':
|
||||
'ca', #Belarusian, Bemba, Bulgarian, Bengali, Breton, Bosnian, Catalan
|
||||
'ca': 'ca',
|
||||
'chr': 'chr',
|
||||
'ckb': 'ckb',
|
||||
'co': 'co',
|
||||
'crs': 'crs',
|
||||
'cs': 'cs',
|
||||
'cy': 'cy',
|
||||
'da':
|
||||
'da', #Cherokee, Kurdish (Sorani), Corsican, Seychellois Creole, Czech, Welsh, Danish
|
||||
'da': 'da',
|
||||
'de': 'de',
|
||||
'ee': 'ee',
|
||||
'el': 'el',
|
||||
'en': 'en',
|
||||
'en-gb': 'en-GB',
|
||||
'en-us': 'en-US',
|
||||
'eo':
|
||||
'eo', #German, Ewe, Greek, English, English (UK), English (US), Esperanto
|
||||
'eo': 'eo',
|
||||
'es': 'es',
|
||||
'es-419': 'es-419',
|
||||
'et': 'et',
|
||||
'eu': 'eu',
|
||||
'fa': 'fa',
|
||||
'fi': 'fi',
|
||||
'fo':
|
||||
'fo', #Spanish, Spanish (Latin American), Estonian, Basque, Persian, Finnish, Faroese
|
||||
'fo': 'fo',
|
||||
'fr': 'fr',
|
||||
'fr-ca': 'fr-ca',
|
||||
'fy': 'fy',
|
||||
'ga': 'ga',
|
||||
'gaa': 'gaa',
|
||||
'gd': 'gd',
|
||||
'gl':
|
||||
'gl', #French, French (Canada), Frisian, Irish, Ga, Scots Gaelic, Galician
|
||||
'gl': 'gl',
|
||||
'gn': 'gn',
|
||||
'gu': 'gu',
|
||||
'ha': 'ha',
|
||||
'haw': 'haw',
|
||||
'he': 'he',
|
||||
'hi': 'hi',
|
||||
'hr': 'hr', #Guarani, Gujarati, Hausa, Hawaiian, Hebrew, Hindi, Croatian
|
||||
'hr': 'hr',
|
||||
'ht': 'ht',
|
||||
'hu': 'hu',
|
||||
'hy': 'hy',
|
||||
'ia': 'ia',
|
||||
'id': 'id',
|
||||
'ig': 'ig',
|
||||
'in':
|
||||
'in', #Haitian Creole, Hungarian, Armenian, Interlingua, Indonesian, Igbo, in
|
||||
'in': 'in',
|
||||
'is': 'is',
|
||||
'it': 'it',
|
||||
'iw': 'iw',
|
||||
'ja': 'ja',
|
||||
'jw': 'jw',
|
||||
'ka': 'ka',
|
||||
'kg':
|
||||
'kg', #Icelandic, Italian, Hebrew, Japanese, Javanese, Georgian, Kongo
|
||||
'kg': 'kg',
|
||||
'kk': 'kk',
|
||||
'km': 'km',
|
||||
'kn': 'kn',
|
||||
'ko': 'ko',
|
||||
'kri': 'kri',
|
||||
'ku': 'ku',
|
||||
'ky':
|
||||
'ky', #Kazakh, Khmer, Kannada, Korean, Krio (Sierra Leone), Kurdish, Kyrgyz
|
||||
'ky': 'ky',
|
||||
'la': 'la',
|
||||
'lg': 'lg',
|
||||
'ln': 'ln',
|
||||
'lo': 'lo',
|
||||
'loz': 'loz',
|
||||
'lt': 'lt',
|
||||
'lua':
|
||||
'lua', #Latin, Luganda, Lingala, Laothian, Lozi, Lithuanian, Tshiluba
|
||||
'lua': 'lua',
|
||||
'lv': 'lv',
|
||||
'mfe': 'mfe',
|
||||
'mg': 'mg',
|
||||
'mi': 'mi',
|
||||
'mk': 'mk',
|
||||
'ml': 'ml',
|
||||
'mn':
|
||||
'mn', #Latvian, Mauritian Creole, Malagasy, Maori, Macedonian, Malayalam, Mongolian
|
||||
'mn': 'mn',
|
||||
'mo': 'mo',
|
||||
'mr': 'mr',
|
||||
'ms': 'ms',
|
||||
'mt': 'mt',
|
||||
'my': 'my',
|
||||
'ne': 'ne',
|
||||
'nl': 'nl', #Moldavian, Marathi, Malay, Maltese, Burmese, Nepali, Dutch
|
||||
'nl': 'nl',
|
||||
'nn': 'nn',
|
||||
'no': 'no',
|
||||
'nso': 'nso',
|
||||
'ny': 'ny',
|
||||
'nyn': 'nyn',
|
||||
'oc': 'oc',
|
||||
'om':
|
||||
'om', #Norwegian (Nynorsk), Norwegian, Northern Sotho, Chichewa, Runyakitara, Occitan, Oromo
|
||||
'om': 'om',
|
||||
'or': 'or',
|
||||
'pa': 'pa',
|
||||
'pcm': 'pcm',
|
||||
'pl': 'pl',
|
||||
'ps': 'ps',
|
||||
'pt-br': 'pt-BR',
|
||||
'pt-pt':
|
||||
'pt-PT', #Oriya, Punjabi, Nigerian Pidgin, Polish, Pashto, Portuguese (Brazil), Portuguese (Portugal)
|
||||
'pt-pt': 'pt-PT',
|
||||
'qu': 'qu',
|
||||
'rm': 'rm',
|
||||
'rn': 'rn',
|
||||
'ro': 'ro',
|
||||
'ru': 'ru',
|
||||
'rw': 'rw',
|
||||
'sd':
|
||||
'sd', #Quechua, Romansh, Kirundi, Romanian, Russian, Kinyarwanda, Sindhi
|
||||
'sd': 'sd',
|
||||
'sh': 'sh',
|
||||
'si': 'si',
|
||||
'sk': 'sk',
|
||||
'sl': 'sl',
|
||||
'sn': 'sn',
|
||||
'so': 'so',
|
||||
'sq':
|
||||
'sq', #Serbo-Croatian, Sinhalese, Slovak, Slovenian, Shona, Somali, Albanian
|
||||
'sq': 'sq',
|
||||
'sr': 'sr',
|
||||
'sr-me': 'sr-ME',
|
||||
'st': 'st',
|
||||
'su': 'su',
|
||||
'sv': 'sv',
|
||||
'sw': 'sw',
|
||||
'ta':
|
||||
'ta', #Serbian, Montenegrin, Sesotho, Sundanese, Swedish, Swahili, Tamil
|
||||
'ta': 'ta',
|
||||
'te': 'te',
|
||||
'tg': 'tg',
|
||||
'th': 'th',
|
||||
'ti': 'ti',
|
||||
'tk': 'tk',
|
||||
'tl': 'tl',
|
||||
'tn': 'tn', #Telugu, Tajik, Thai, Tigrinya, Turkmen, Tagalog, Setswana
|
||||
'tn': 'tn',
|
||||
'to': 'to',
|
||||
'tr': 'tr',
|
||||
'tt': 'tt',
|
||||
'tum': 'tum',
|
||||
'tw': 'tw',
|
||||
'ug': 'ug',
|
||||
'uk': 'uk', #Tonga, Turkish, Tatar, Tumbuka, Twi, Uighur, Ukrainian
|
||||
'uk': 'uk',
|
||||
'ur': 'ur',
|
||||
'uz': 'uz',
|
||||
'vi': 'vi',
|
||||
'wo': 'wo',
|
||||
'xh': 'xh',
|
||||
'yi': 'yi',
|
||||
'yo': 'yo', #Urdu, Uzbek, Vietnamese, Wolof, Xhosa, Yiddish, Yoruba
|
||||
'yo': 'yo',
|
||||
'zh-cn': 'zh-CN',
|
||||
'zh-hk': 'zh-HK',
|
||||
'zh-tw': 'zh-TW',
|
||||
'zu':
|
||||
'zu', #Chinese (Simplified), Chinese (Hong Kong/Traditional), Chinese (Taiwan/Traditional), Zulu
|
||||
'zu': 'zu',
|
||||
}
|
||||
|
||||
# maxResults exception values for API list calls. Should only be listed if:
|
||||
|
||||
Reference in New Issue
Block a user