mirror of
https://github.com/nikdoof/foursquare-feeds.git
synced 2025-12-13 08:52:23 +00:00
1
Pipfile
1
Pipfile
@@ -9,6 +9,7 @@ verify_ssl = true
|
||||
foursquare = "*"
|
||||
ics = {editable = true,git = "https://github.com/C4ptainCrunch/ics.py.git",ref = "bd918ec7453a7cf73a906cdcc78bd88eb4bab71b"}
|
||||
simplekml = "*"
|
||||
pytz = "*"
|
||||
|
||||
[requires]
|
||||
python_version = "3.6"
|
||||
|
||||
10
Pipfile.lock
generated
10
Pipfile.lock
generated
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"_meta": {
|
||||
"hash": {
|
||||
"sha256": "67eeb6f5b773d0e59e35ec16559d97c999f7a7b2862284392d7fdf65a280185e"
|
||||
"sha256": "3a9e4292a546993c8c1f3f01a7183f293efeb295fd8fa776b213273b95279572"
|
||||
},
|
||||
"pipfile-spec": 6,
|
||||
"requires": {
|
||||
@@ -63,6 +63,14 @@
|
||||
],
|
||||
"version": "==2.8.0"
|
||||
},
|
||||
"pytz": {
|
||||
"hashes": [
|
||||
"sha256:26c0b32e437e54a18161324a2fca3c4b9846b74a8dccddd843113109e1116b32",
|
||||
"sha256:c894d57500a4cd2d5c71114aaab77dbab5eabd9022308ce5ac9bb93a60a6f0c7"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==2019.2"
|
||||
},
|
||||
"requests": {
|
||||
"hashes": [
|
||||
"sha256:11e007a8a2aa0323f5a921e9e6a2d7e4e67d9877e85773fba9ba6419025cbeb4",
|
||||
|
||||
@@ -1,11 +1,15 @@
|
||||
#!/usr/bin/env python3
|
||||
import argparse
|
||||
import configparser
|
||||
from datetime import datetime
|
||||
import logging
|
||||
import os
|
||||
import pytz
|
||||
from xml.sax.saxutils import escape as xml_escape
|
||||
|
||||
import foursquare
|
||||
from ics import Calendar, Event
|
||||
import simplekml
|
||||
|
||||
logging.basicConfig(level=logging.INFO, format="%(message)s")
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -59,16 +63,11 @@ class FeedGenerator:
|
||||
logger.info(f"Fetched {checkins} checkin{plural} from the API")
|
||||
|
||||
if kind == "ics":
|
||||
data = self._generate_calendar(checkins)
|
||||
filepath = self.ics_filepath
|
||||
filepath = self._generate_ics_file(checkins)
|
||||
elif kind == "kml":
|
||||
data = self._generate_kml(checkins)
|
||||
filepath = self.kml_filepath
|
||||
filepath = self._generate_kml_file(checkins)
|
||||
|
||||
with open(filepath, "w") as f:
|
||||
f.writelines(data)
|
||||
|
||||
logger.info(f"Generated {kind} file {filepath}")
|
||||
logger.info(f"Generated file {filepath}")
|
||||
|
||||
exit(0)
|
||||
|
||||
@@ -117,15 +116,31 @@ class FeedGenerator:
|
||||
logger.error(f"Error getting checkins, with offset of {offset}: {err}")
|
||||
exit(1)
|
||||
|
||||
def _get_user_url(self):
|
||||
"Returns the Foursquare URL for the authenticated user."
|
||||
def _get_user(self):
|
||||
"Returns details about the authenticated user."
|
||||
try:
|
||||
user = self.client.users()
|
||||
except foursquare.FoursquareException as err:
|
||||
logger.error(f"Error getting user: {err}")
|
||||
exit(1)
|
||||
|
||||
return user["user"]["canonicalUrl"]
|
||||
return user["user"]
|
||||
|
||||
def _generate_ics_file(self, checkins):
|
||||
"""Supplied with a list of checkin data from the API, generates
|
||||
and saves a .ics file.
|
||||
|
||||
Returns the filepath of the saved file.
|
||||
|
||||
Keyword arguments:
|
||||
checkins -- A list of dicts, each one data about a single checkin.
|
||||
"""
|
||||
calendar = self._generate_calendar(checkins)
|
||||
|
||||
with open(self.ics_filepath, "w") as f:
|
||||
f.writelines(calendar)
|
||||
|
||||
return self.ics_filepath
|
||||
|
||||
def _generate_calendar(self, checkins):
|
||||
"""Supplied with a list of checkin data from the API, generates
|
||||
@@ -134,7 +149,7 @@ class FeedGenerator:
|
||||
Keyword arguments:
|
||||
checkins -- A list of dicts, each one data about a single checkin.
|
||||
"""
|
||||
user_url = self._get_user_url()
|
||||
user = self._get_user()
|
||||
|
||||
c = Calendar()
|
||||
|
||||
@@ -143,6 +158,7 @@ class FeedGenerator:
|
||||
# I had some checkins with no data other than
|
||||
# id, createdAt and source.
|
||||
continue
|
||||
|
||||
venue_name = checkin["venue"]["name"]
|
||||
tz_offset = self._get_checkin_timezone(checkin)
|
||||
|
||||
@@ -150,7 +166,7 @@ class FeedGenerator:
|
||||
|
||||
e.name = f"@ {venue_name}"
|
||||
e.location = venue_name
|
||||
e.url = f"{user_url}/checkin/{checkin['id']}"
|
||||
e.url = f"{user['canonicalUrl']}/checkin/{checkin['id']}"
|
||||
e.uid = f"{checkin['id']}@foursquare.com"
|
||||
e.begin = checkin["createdAt"]
|
||||
|
||||
@@ -175,15 +191,76 @@ class FeedGenerator:
|
||||
|
||||
return c
|
||||
|
||||
def _generate_kml(self, checkins):
|
||||
def _generate_kml_file(self, checkins):
|
||||
"""Supplied with a list of checkin data from the API, generates
|
||||
a TODO
|
||||
and saves a kml file.
|
||||
|
||||
Returns the filepath of the saved file.
|
||||
|
||||
Keyword arguments:
|
||||
checkins -- A list of dicts, each one data about a single checkin.
|
||||
"""
|
||||
user = self._get_user()
|
||||
|
||||
return None
|
||||
kml = simplekml.Kml()
|
||||
|
||||
# The original Foursquare files had a Folder with name and
|
||||
# description like this, so:
|
||||
user_name = f"{user['firstName']} {user['lastName']}"
|
||||
name = f"foursquare checkin history for {user_name}"
|
||||
fol = kml.newfolder(name=name, description=name)
|
||||
|
||||
for checkin in checkins:
|
||||
if "venue" not in checkin:
|
||||
# I had some checkins with no data other than
|
||||
# id, createdAt and source.
|
||||
continue
|
||||
|
||||
venue_name = checkin["venue"]["name"]
|
||||
tz_offset = self._get_checkin_timezone(checkin)
|
||||
url = f'https://foursquare.com/v/{checkin["venue"]["id"]}'
|
||||
|
||||
description = [f'@<a href="{url}">{venue_name}</a>']
|
||||
if "shout" in checkin and len(checkin["shout"]) > 0:
|
||||
description.append('"{}"'.format(checkin["shout"]))
|
||||
description.append(f"Timezone offset: {tz_offset}")
|
||||
|
||||
coords = [
|
||||
(
|
||||
checkin["venue"]["location"]["lng"],
|
||||
checkin["venue"]["location"]["lat"],
|
||||
)
|
||||
]
|
||||
|
||||
pnt = fol.newpoint(
|
||||
name=venue_name,
|
||||
description="<![CDATA[{}]]>".format('\n'.join(description)),
|
||||
coords=coords,
|
||||
# Both of these were set like this in Foursquare's original KML:
|
||||
altitudemode=simplekml.AltitudeMode.relativetoground,
|
||||
extrude=1,
|
||||
)
|
||||
|
||||
# Foursquare's KML feeds had 'updated' and 'published' elements
|
||||
# in the Placemark, but I don't *think* those are standard, so:
|
||||
pnt.timestamp.when = (
|
||||
datetime.utcfromtimestamp(checkin["createdAt"])
|
||||
.replace(tzinfo=pytz.utc)
|
||||
.isoformat()
|
||||
)
|
||||
|
||||
# Use the address, if any:
|
||||
if "location" in checkin["venue"]:
|
||||
loc = checkin["venue"]["location"]
|
||||
if "formattedAddress" in loc and len(loc["formattedAddress"]) > 0:
|
||||
address = ", ".join(loc["formattedAddress"])
|
||||
# While simplexml escapes other strings, it threw a wobbly
|
||||
# over '&' in addresses, so escape them:
|
||||
pnt.address = xml_escape(address)
|
||||
|
||||
kml.save(self.kml_filepath)
|
||||
|
||||
return self.kml_filepath
|
||||
|
||||
def _get_checkin_timezone(self, checkin):
|
||||
"""Given a checkin from the API, returns a string representing the
|
||||
|
||||
Reference in New Issue
Block a user