2021-08-31 16:18:41 +02:00
|
|
|
from math import radians, cos, sqrt
|
2021-09-26 17:44:13 +02:00
|
|
|
from datetime import datetime
|
|
|
|
import hashlib
|
2022-04-18 20:14:51 +02:00
|
|
|
import os
|
|
|
|
import platform
|
2021-09-12 07:39:37 +02:00
|
|
|
import re
|
2022-04-18 20:14:51 +02:00
|
|
|
import subprocess
|
2021-08-31 16:18:41 +02:00
|
|
|
|
2021-09-26 17:44:13 +02:00
|
|
|
|
|
|
|
def checksum(file_path, blocksize=65536):
|
|
|
|
"""Create a hash value for the given file.
|
|
|
|
|
|
|
|
See http://stackoverflow.com/a/3431835/1318758.
|
|
|
|
|
|
|
|
:param str file_path: Path to the file to create a hash for.
|
|
|
|
:param int blocksize: Read blocks of this size from the file when
|
|
|
|
creating the hash.
|
|
|
|
:returns: str or None
|
|
|
|
"""
|
|
|
|
hasher = hashlib.sha256()
|
2021-11-12 09:39:08 +01:00
|
|
|
with open(file_path, 'rb') as file:
|
|
|
|
buf = file.read(blocksize)
|
2021-09-26 17:44:13 +02:00
|
|
|
|
|
|
|
while len(buf) > 0:
|
|
|
|
hasher.update(buf)
|
2021-11-12 09:39:08 +01:00
|
|
|
buf = file.read(blocksize)
|
2021-09-26 17:44:13 +02:00
|
|
|
return hasher.hexdigest()
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
2021-08-31 16:18:41 +02:00
|
|
|
def distance_between_two_points(lat1, lon1, lat2, lon2):
|
2021-11-12 09:39:08 +01:00
|
|
|
"""Return distance between two points"""
|
2021-08-31 16:18:41 +02:00
|
|
|
# From http://stackoverflow.com/questions/15736995/how-can-i-quickly-estimate-the-distance-between-two-latitude-longitude-points # noqa
|
2021-11-12 09:39:08 +01:00
|
|
|
|
2021-08-31 16:18:41 +02:00
|
|
|
# convert decimal degrees to radians
|
2021-10-15 19:56:50 +02:00
|
|
|
lat1, lon1, lat2, lon2 = list(map(radians, [lat1, lon1, lat2, lon2]))
|
2021-08-31 16:18:41 +02:00
|
|
|
|
2021-11-12 09:39:08 +01:00
|
|
|
rad = 6371000 # radius of the earth in m
|
2021-08-31 16:18:41 +02:00
|
|
|
x = (lon2 - lon1) * cos(0.5 * (lat2 + lat1))
|
|
|
|
y = lat2 - lat1
|
2021-11-12 09:39:08 +01:00
|
|
|
return rad * sqrt(x * x + y * y)
|
2021-09-12 07:39:37 +02:00
|
|
|
|
2021-10-15 19:56:50 +02:00
|
|
|
|
2021-12-05 18:27:04 +01:00
|
|
|
def empty_dir(dir_path):
|
|
|
|
return not next(os.scandir(dir_path), None)
|
|
|
|
|
|
|
|
|
2022-04-17 21:58:56 +02:00
|
|
|
def filename_filter(filename):
|
|
|
|
"""
|
|
|
|
Take a string and return a valid filename constructed from the string.
|
|
|
|
"""
|
|
|
|
blacklist = '/\\:*"<>|'
|
|
|
|
if filename is None:
|
|
|
|
return filename
|
|
|
|
|
|
|
|
# Remove blacklisted chars.
|
|
|
|
for char in blacklist:
|
|
|
|
filename = filename.replace(char, '')
|
|
|
|
|
|
|
|
return filename
|
|
|
|
|
|
|
|
|
2021-11-12 09:17:38 +01:00
|
|
|
def get_date_regex(user_regex=None):
|
|
|
|
"""Return date regex generator"""
|
2021-11-11 18:54:41 +01:00
|
|
|
if user_regex:
|
|
|
|
regex = {'a': re.compile(user_regex)}
|
2021-09-12 07:39:37 +02:00
|
|
|
else:
|
|
|
|
regex = {
|
|
|
|
# regex to match date format type %Y%m%d, %y%m%d, %d%m%Y,
|
|
|
|
# etc...
|
|
|
|
'a': re.compile(
|
2022-07-23 20:15:34 +02:00
|
|
|
r'[-_./ ](?P<year>\d{4})[-_.]?(?P<month>\d{2})[-_.]?(?P<day>\d{2})[-_.]?(?P<hour>\d{2})[-_.]?(?P<minute>\d{2})[-_.]?(?P<second>\d{2})([-_./ ])'
|
2021-10-15 19:56:50 +02:00
|
|
|
),
|
|
|
|
'b': re.compile(
|
2022-07-23 20:15:34 +02:00
|
|
|
r'[-_./ ](?P<year>\d{4})[-_.]?(?P<month>\d{2})[-_.]?(?P<day>\d{2})([-_./ ])'
|
2021-10-15 19:56:50 +02:00
|
|
|
),
|
2021-09-12 07:39:37 +02:00
|
|
|
# not very accurate
|
2021-10-15 19:56:50 +02:00
|
|
|
'c': re.compile(
|
2022-07-23 20:15:34 +02:00
|
|
|
r'[-_./ ](?P<year>\d{2})[-_.]?(?P<month>\d{2})[-_.]?(?P<day>\d{2})([-_./ ])'
|
2021-10-15 19:56:50 +02:00
|
|
|
),
|
|
|
|
'd': re.compile(
|
2022-07-23 20:15:34 +02:00
|
|
|
r'[-_./ ](?P<day>\d{2})[-_.](?P<month>\d{2})[-_.](?P<year>\d{4})([-_./ ])'
|
2021-10-15 19:56:50 +02:00
|
|
|
),
|
|
|
|
}
|
2021-09-12 07:39:37 +02:00
|
|
|
|
2021-11-11 18:54:41 +01:00
|
|
|
return regex
|
2021-09-12 07:39:37 +02:00
|
|
|
|
2021-09-26 17:44:13 +02:00
|
|
|
|
2022-04-18 20:14:51 +02:00
|
|
|
DATE_REGEX = get_date_regex()
|
|
|
|
|
|
|
|
|
|
|
|
def get_date_from_string(string):
|
2021-11-12 09:17:38 +01:00
|
|
|
"""Retrieve date stamp from string"""
|
2021-09-12 07:39:37 +02:00
|
|
|
# If missing datetime from EXIF data check if filename is in datetime format.
|
|
|
|
# For this use a user provided regex if possible.
|
|
|
|
# Otherwise assume a filename such as IMG_20160915_123456.jpg as default.
|
|
|
|
|
|
|
|
matches = []
|
2022-07-23 20:15:34 +02:00
|
|
|
sep = ''
|
2022-04-18 20:14:51 +02:00
|
|
|
for i, regex in DATE_REGEX.items():
|
2021-11-12 09:17:38 +01:00
|
|
|
match = re.findall(regex, string)
|
2021-09-12 07:39:37 +02:00
|
|
|
if match != []:
|
2022-07-23 20:15:34 +02:00
|
|
|
sep = match[0][3]
|
2021-09-12 07:39:37 +02:00
|
|
|
if i == 'c':
|
|
|
|
match = [('20' + match[0][0], match[0][1], match[0][2])]
|
|
|
|
elif i == 'd':
|
|
|
|
# reorder items
|
|
|
|
match = [(match[0][2], match[0][1], match[0][0])]
|
2022-07-23 20:15:34 +02:00
|
|
|
else:
|
|
|
|
match = [(match[0][0], match[0][1], match[0][2])]
|
2021-09-12 07:39:37 +02:00
|
|
|
if len(match) != 1:
|
|
|
|
# The time string is not uniq
|
|
|
|
continue
|
2021-11-12 09:17:38 +01:00
|
|
|
matches.append((match[0], regex))
|
2021-09-12 07:39:37 +02:00
|
|
|
# We want only the first match for the moment
|
|
|
|
break
|
|
|
|
|
|
|
|
# check if there is only one result
|
|
|
|
if len(set(matches)) == 1:
|
|
|
|
try:
|
|
|
|
# Convert str to int
|
|
|
|
date_object = tuple(map(int, matches[0][0]))
|
|
|
|
date = datetime(*date_object)
|
|
|
|
except (KeyError, ValueError):
|
2022-07-23 20:15:34 +02:00
|
|
|
return None, matches[0][1], sep
|
2021-09-12 07:39:37 +02:00
|
|
|
|
2022-07-23 20:15:34 +02:00
|
|
|
return date, matches[0][1], sep
|
|
|
|
|
|
|
|
return None, None, sep
|
2021-09-12 07:39:37 +02:00
|
|
|
|
2021-09-26 17:44:13 +02:00
|
|
|
|
2022-04-18 20:14:51 +02:00
|
|
|
def match_date_regex(regex, value):
|
|
|
|
if re.match(regex, value) is not None:
|
|
|
|
return re.sub(regex, r'\g<1>-\g<2>-\g<3>-', value)
|
|
|
|
|
|
|
|
return value
|
|
|
|
|
|
|
|
|
2021-11-06 16:35:35 +01:00
|
|
|
def split_part(dedup_regex, path_part, items=None):
|
|
|
|
"""
|
|
|
|
Split part from regex
|
|
|
|
:returns: parts
|
|
|
|
"""
|
|
|
|
if not items:
|
|
|
|
items = []
|
|
|
|
|
2021-11-11 18:54:41 +01:00
|
|
|
regex = dedup_regex.pop()
|
2021-11-06 16:35:35 +01:00
|
|
|
parts = re.split(regex, path_part)
|
|
|
|
# Loop thought part, search matched regex part and proceed with
|
|
|
|
# next regex for others parts
|
|
|
|
for n, part in enumerate(parts):
|
|
|
|
if re.match(regex, part):
|
|
|
|
if part[0] in '-_ .':
|
|
|
|
if n > 0:
|
|
|
|
# move the separator to previous item
|
|
|
|
parts[n - 1] = parts[n - 1] + part[0]
|
|
|
|
items.append(part[1:])
|
|
|
|
else:
|
|
|
|
items.append(part)
|
2021-11-11 18:54:41 +01:00
|
|
|
elif dedup_regex:
|
2021-11-06 16:35:35 +01:00
|
|
|
# Others parts
|
|
|
|
items = split_part(dedup_regex, part, items)
|
|
|
|
else:
|
|
|
|
items.append(part)
|
|
|
|
|
|
|
|
return items
|
|
|
|
|
|
|
|
|
2021-09-12 07:41:44 +02:00
|
|
|
# Conversion functions
|
|
|
|
# source:https://rodic.fr/blog/camelcase-and-snake_case-strings-conversion-with-python/
|
|
|
|
|
2021-10-15 19:56:50 +02:00
|
|
|
|
2021-09-12 07:41:44 +02:00
|
|
|
def snake2camel(name):
|
|
|
|
return re.sub(r'(?:^|_)([a-z])', lambda x: x.group(1).upper(), name)
|
|
|
|
|
2021-09-26 17:44:13 +02:00
|
|
|
|
2021-10-15 19:56:50 +02:00
|
|
|
def camel2snake(name):
|
|
|
|
return name[0].lower() + re.sub(
|
|
|
|
r'(?!^)[A-Z]', lambda x: '_' + x.group(0).lower(), name[1:]
|
|
|
|
)
|
2021-11-13 10:03:53 +01:00
|
|
|
|
|
|
|
|
|
|
|
def open_file(path):
|
|
|
|
if platform.system() == "Windows":
|
|
|
|
os.startfile(path)
|
|
|
|
elif platform.system() == "Darwin":
|
|
|
|
subprocess.Popen(["open", path])
|
|
|
|
else:
|
|
|
|
subprocess.Popen(["xdg-open", path])
|