From 79e1633ebc2733b473d44306d8158c80b462cc71 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Mon, 10 May 2021 19:53:20 +0100 Subject: [PATCH] Move city functions to their own module --- city.py | 147 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ media.py | 137 +-------------------------------------------------- tests.py | 2 +- 3 files changed, 149 insertions(+), 137 deletions(-) create mode 100644 city.py diff --git a/city.py b/city.py new file mode 100644 index 000000000..b8e3a2186 --- /dev/null +++ b/city.py @@ -0,0 +1,147 @@ +__filename__ = "city.py" +__author__ = "Bob Mottram" +__license__ = "AGPL3+" +__version__ = "1.2.0" +__maintainer__ = "Bob Mottram" +__email__ = "bob@freedombone.net" +__status__ = "Production" + +import os +import datetime +import random +import math +from random import randint + + +def _getCityPulse(currTimeOfDay, decoySeed: int) -> (float, float): + """The data decoy + This simulates expected average patterns of movement in a city. + Jane or Joe average lives and works in the city, commuting in + and out of the central district for work. They have a unique + life pattern, which machine learning can latch onto. + This returns a polar coordinate: + Distance from the city centre is in the range 0.0 - 1.0 + Angle is in radians + """ + randgen = random.Random(decoySeed) + variance = 3 + busyStates = ("work", "shop", "play", "party") + dataDecoyState = "sleep" + dataDecoyIndex = 0 + weekday = currTimeOfDay.weekday() + minHour = 7 + randint(0, variance) + maxHour = 17 + randint(0, variance) + if currTimeOfDay.hour > minHour: + if currTimeOfDay.hour <= maxHour: + if weekday < 5: + dataDecoyState = "work" + dataDecoyIndex = 1 + elif weekday == 5: + dataDecoyState = "shop" + dataDecoyIndex = 2 + else: + dataDecoyState = "play" + dataDecoyIndex = 3 + else: + if weekday < 5: + dataDecoyState = "evening" + dataDecoyIndex = 4 + else: + dataDecoyState = "party" + dataDecoyIndex = 5 + randgen2 = random.Random(decoySeed + dataDecoyIndex) + angleRadians = \ + (randgen2.randint(0, 100000) / 100000) * 2 * math.pi + # some people are quite random, others have more predictable habits + decoyRandomness = randgen.randint(1, 3) + # occasionally throw in a wildcard to keep the machine learning guessing + if randint(0, 100) < decoyRandomness: + distanceFromCityCenter = (randint(0, 100000) / 100000) + angleRadians = (randint(0, 100000) / 100000) * 2 * math.pi + else: + # what consitutes the central district is fuzzy + centralDistrictFuzz = (randgen.randint(0, 100000) / 100000) * 0.1 + busyRadius = 0.3 + centralDistrictFuzz + if dataDecoyState in busyStates: + # if we are busy then we're somewhere in the city center + distanceFromCityCenter = \ + (randgen.randint(0, 100000) / 100000) * busyRadius + else: + # otherwise we're in the burbs + distanceFromCityCenter = busyRadius + \ + ((1.0 - busyRadius) * (randgen.randint(0, 100000) / 100000)) + return distanceFromCityCenter, angleRadians + + +def spoofGeolocation(baseDir: str, + city: str, currTime, decoySeed: int, + citiesList: []) -> (float, float, str, str): + """Given a city and the current time spoofs the location + for an image + returns latitude, longitude, N/S, E/W + """ + locationsFilename = baseDir + '/custom_locations.txt' + if not os.path.isfile(locationsFilename): + locationsFilename = baseDir + '/locations.txt' + cityRadius = 0.1 + variance = 0.001 + default_latitude = 51.8744 + default_longitude = 0.368333 + default_latdirection = 'N' + default_longdirection = 'W' + + if citiesList: + cities = citiesList + else: + if not os.path.isfile(locationsFilename): + return (default_latitude, default_longitude, + default_latdirection, default_longdirection) + cities = [] + with open(locationsFilename, "r") as f: + cities = f.readlines() + + city = city.lower() + for cityName in cities: + if city in cityName.lower(): + latitude = cityName.split(':')[1] + longitude = cityName.split(':')[2] + latdirection = 'N' + longdirection = 'E' + if 'S' in latitude: + latdirection = 'S' + latitude = latitude.replace('S', '') + if 'W' in longitude: + longdirection = 'W' + longitude = longitude.replace('W', '') + latitude = float(latitude) + longitude = float(longitude) + # get the time of day at the city + approxTimeZone = int(longitude / 15.0) + if longdirection == 'E': + approxTimeZone = -approxTimeZone + currTimeAdjusted = currTime - \ + datetime.timedelta(hours=approxTimeZone) + # patterns of activity change in the city over time + (distanceFromCityCenter, angleRadians) = \ + _getCityPulse(currTimeAdjusted, decoySeed) + # Get the position within the city, with some randomness added + latitude += \ + distanceFromCityCenter * cityRadius * math.cos(angleRadians) + # add a small amount of variance around the location + fraction = randint(0, 100000) / 100000 + latitude += (fraction * fraction * variance) - (variance / 2.0) + + longitude += \ + distanceFromCityCenter * cityRadius * math.sin(angleRadians) + # add a small amount of variance around the location + fraction = randint(0, 100000) / 100000 + longitude += (fraction * fraction * variance) - (variance / 2.0) + + # gps locations aren't transcendental, so round to a fixed + # number of decimal places + latitude = int(latitude * 10000) / 10000.0 + longitude = int(longitude * 10000) / 10000.0 + return latitude, longitude, latdirection, longdirection + + return (default_latitude, default_longitude, + default_latdirection, default_longdirection) diff --git a/media.py b/media.py index f81fe2af1..ac563ffab 100644 --- a/media.py +++ b/media.py @@ -8,8 +8,6 @@ __status__ = "Production" import os import datetime -import random -import math from random import randint from hashlib import sha1 from auth import createPassword @@ -21,6 +19,7 @@ from utils import getMediaExtensions from shutil import copyfile from shutil import rmtree from shutil import move +from city import spoofGeolocation def replaceYouTube(postJsonObject: {}, replacementDomain: str) -> None: @@ -56,140 +55,6 @@ def _removeMetaData(imageFilename: str, outputFilename: str) -> None: os.system('/usr/bin/mogrify -strip ' + outputFilename) # nosec -def _getCityPulse(currTimeOfDay, decoySeed: int) -> (float, float): - """The data decoy - This simulates expected average patterns of movement in a city. - Jane or Joe average lives and works in the city, commuting in - and out of the central district for work. They have a unique - life pattern, which machine learning can latch onto. - This returns a polar coordinate: - Distance from the city centre is in the range 0.0 - 1.0 - Angle is in radians - """ - randgen = random.Random(decoySeed) - variance = 3 - busyStates = ("work", "shop", "play", "party") - dataDecoyState = "sleep" - dataDecoyIndex = 0 - weekday = currTimeOfDay.weekday() - minHour = 7 + randint(0, variance) - maxHour = 17 + randint(0, variance) - if currTimeOfDay.hour > minHour: - if currTimeOfDay.hour <= maxHour: - if weekday < 5: - dataDecoyState = "work" - dataDecoyIndex = 1 - elif weekday == 5: - dataDecoyState = "shop" - dataDecoyIndex = 2 - else: - dataDecoyState = "play" - dataDecoyIndex = 3 - else: - if weekday < 5: - dataDecoyState = "evening" - dataDecoyIndex = 4 - else: - dataDecoyState = "party" - dataDecoyIndex = 5 - randgen2 = random.Random(decoySeed + dataDecoyIndex) - angleRadians = \ - (randgen2.randint(0, 100000) / 100000) * 2 * math.pi - # some people are quite random, others have more predictable habits - decoyRandomness = randgen.randint(1, 3) - # occasionally throw in a wildcard to keep the machine learning guessing - if randint(0, 100) < decoyRandomness: - distanceFromCityCenter = (randint(0, 100000) / 100000) - angleRadians = (randint(0, 100000) / 100000) * 2 * math.pi - else: - # what consitutes the central district is fuzzy - centralDistrictFuzz = (randgen.randint(0, 100000) / 100000) * 0.1 - busyRadius = 0.3 + centralDistrictFuzz - if dataDecoyState in busyStates: - # if we are busy then we're somewhere in the city center - distanceFromCityCenter = \ - (randgen.randint(0, 100000) / 100000) * busyRadius - else: - # otherwise we're in the burbs - distanceFromCityCenter = busyRadius + \ - ((1.0 - busyRadius) * (randgen.randint(0, 100000) / 100000)) - return distanceFromCityCenter, angleRadians - - -def spoofGeolocation(baseDir: str, - city: str, currTime, decoySeed: int, - citiesList: []) -> (float, float, str, str): - """Given a city and the current time spoofs the location - for an image - returns latitude, longitude, N/S, E/W - """ - locationsFilename = baseDir + '/custom_locations.txt' - if not os.path.isfile(locationsFilename): - locationsFilename = baseDir + '/locations.txt' - cityRadius = 0.1 - variance = 0.001 - default_latitude = 51.8744 - default_longitude = 0.368333 - default_latdirection = 'N' - default_longdirection = 'W' - - if citiesList: - cities = citiesList - else: - if not os.path.isfile(locationsFilename): - return (default_latitude, default_longitude, - default_latdirection, default_longdirection) - cities = [] - with open(locationsFilename, "r") as f: - cities = f.readlines() - - city = city.lower() - for cityName in cities: - if city in cityName.lower(): - latitude = cityName.split(':')[1] - longitude = cityName.split(':')[2] - latdirection = 'N' - longdirection = 'E' - if 'S' in latitude: - latdirection = 'S' - latitude = latitude.replace('S', '') - if 'W' in longitude: - longdirection = 'W' - longitude = longitude.replace('W', '') - latitude = float(latitude) - longitude = float(longitude) - # get the time of day at the city - approxTimeZone = int(longitude / 15.0) - if longdirection == 'E': - approxTimeZone = -approxTimeZone - currTimeAdjusted = currTime - \ - datetime.timedelta(hours=approxTimeZone) - # patterns of activity change in the city over time - (distanceFromCityCenter, angleRadians) = \ - _getCityPulse(currTimeAdjusted, decoySeed) - # Get the position within the city, with some randomness added - latitude += \ - distanceFromCityCenter * cityRadius * math.cos(angleRadians) - # add a small amount of variance around the location - fraction = randint(0, 100000) / 100000 - latitude += (fraction * fraction * variance) - (variance / 2.0) - - longitude += \ - distanceFromCityCenter * cityRadius * math.sin(angleRadians) - # add a small amount of variance around the location - fraction = randint(0, 100000) / 100000 - longitude += (fraction * fraction * variance) - (variance / 2.0) - - # gps locations aren't transcendental, so round to a fixed - # number of decimal places - latitude = int(latitude * 10000) / 10000.0 - longitude = int(longitude * 10000) / 10000.0 - return latitude, longitude, latdirection, longdirection - - return (default_latitude, default_longitude, - default_latdirection, default_longdirection) - - def _spoofMetaData(baseDir: str, nickname: str, domain: str, outputFilename: str, spoofCity: str) -> None: """Spoof image metadata using a decoy model for a given city diff --git a/tests.py b/tests.py index 88136a331..88d3c2fcd 100644 --- a/tests.py +++ b/tests.py @@ -77,7 +77,7 @@ from like import likePost from like import sendLikeViaServer from announce import announcePublic from announce import sendAnnounceViaServer -from media import spoofGeolocation +from city import spoofGeolocation from media import getMediaPath from media import getAttachmentMediaType from delete import sendDeleteViaServer