diff --git a/CHANGELOG.md b/CHANGELOG.md
index 1eeab94..7607ab5 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,10 +1,10 @@
# Changelog
-## 2.3.1 - 2025-08-??
+## 2.4.0 - 2025-08-??
- Add support for pushing events to a CalDAV server.
-- Add a more detailed description to the events
--
+- Add a more detailed description to the events.
+- Remove KML support.
## 2.3.0 - 2024-11-30
diff --git a/README.md b/README.md
index 443ea95..0a98889 100644
--- a/README.md
+++ b/README.md
@@ -1,177 +1,182 @@
# Foursquare Feeds
-A Python script that will generate iCal (`.ics`) or KML files of your checkins on [Foursquare][4sq]/[Swarm][swarm].
+A Python tool that downloads your check-ins from [Foursquare][4sq]/[Swarm][swarm] and converts them into calendar events. You can either generate an iCal (`.ics`) file or sync directly to a CalDAV server.
-If you set it up to save the iCal file to a publicly-visible location on a webserver, and run the script regularly, you can subscribe to the feed in your favourite calendar application.
-
-A KML file can be loaded into a mapping application (such as Google Earth or
-Maps) to view the checkins on a map.
-
-Foursquare [used to have such feeds][feeds] but they've stopped working for me.
-[I wrote a bit about this.][blog]
+Perfect for keeping a record of your travels and activities in your preferred calendar application.
[4sq]: https://foursquare.com
[swarm]: https://www.swarmapp.com
-[feeds]: https://foursquare.com/feeds/
-[blog]: https://www.gyford.com/phil/writing/2019/05/13/foursquare-swarm-ical-feed/
+## Features
+
+- **iCal Export**: Generate `.ics` files that can be imported into any calendar application
+- **CalDAV Sync**: Upload check-ins directly to CalDAV servers (Google Calendar, iCloud, etc.)
+- **Flexible Fetching**: Get recent check-ins or your entire history
+- **Rich Event Data**: Includes location details, notes (shouts), and check-in metadata
## Installation
-This should work with python 3.12 (and maybe others).
+This project requires Python 3.12+ and uses [uv](https://github.com/astral-sh/uv) for dependency management.
-### 1. Make a Foursquare app
+### 1. Install uv
-Go to https://foursquare.com/developers/apps and create a new App.
+If you don't have uv installed:
+```bash
+curl -LsSf https://astral.sh/uv/install.sh | sh
+```
-### 2. Install python requirements
+### 2. Set up the project
-Either using [uv](https://github.com/astral-sh/uv):
+Clone the repository and create a virtual environment:
- $ uv sync
+```bash
+git clone https://github.com/nikdoof/foursquare-feeds.git
+cd foursquare-feeds
+uv sync
+```
-or [pip](https://pip.pypa.io/en/stable/):
+### 3. Create a Foursquare app
- $ pip install -r requirements.txt
+1. Go to https://foursquare.com/developers/apps
+2. Create a new App
+3. Note your Client ID and Client Secret
+### 4. Configure the application
-### 3. Set up config file
+Copy the example configuration file:
-Copy `config_example.ini` to `config.ini`.
+```bash
+cp config_example.ini config.ini
+```
-Change the `IcsFilepath` and `KmlFilepath` to wherever you want your files to be saved.
+Edit `config.ini` with your settings:
-To get the `AccessToken` for your Foursquare app, you will have to go through the sometimes laborious procedure in step 4...
+- **AccessToken**: Your Foursquare access token (see below)
+- **IcsFilepath**: Where to save the `.ics` file (for local export)
+- **CalDAV settings**: Your CalDAV server details (for direct sync)
+## Getting an Access Token
-### 4. Get an access token
+You need a Foursquare access token to use this tool. Here are two methods:
-There are two ways to do this: (A) The quick way, using a third-party website or (B) the slow way, on the command line. Use (A) unless the website isn't working.
+### Method A: Quick Web-based Authentication
-#### (A) The quick way
+1. Visit https://your-foursquare-oauth-token.glitch.me
+2. Follow the Foursquare login link
+3. Accept the permissions
+4. Copy the access token into your `config.ini`
-Go to https://your-foursquare-oauth-token.glitch.me and follow the link to log
-in with Foursquare.
+*Thanks to [Simon Willison](https://github.com/dogsheep/swarm-to-sqlite/issues/4) for this tool.*
-Accept the permissions, and then copy the long code, which is your Access
-Token, into your `config.ini`.
+### Method B: Manual OAuth Flow
-That's it. [Thanks to Simon Willison for that.](https://github.com/dogsheep/swarm-to-sqlite/issues/4)
-
-#### (B) The slow way
-
-On https://foursquare.com/developers/apps, in your app, set the Redirect URI to `http://localhost:8000/`
-
-In a terminal window, open a python shell:
-
- $ python
-
-and, using your app's Client ID and Client Secret in place of `YOUR_CLIENT_ID` and `YOUR_CLIENT_SECRET` enter this:
+1. Set your app's Redirect URI to `http://localhost:8000/` in the Foursquare developer console
+2. Run the following Python commands:
```python
import foursquare
-client = foursquare.Foursquare(client_id='YOUR_CLIENT_ID' client_secret='YOUR_CLIENT_SECRET', redirect_uri='http://localhost:8000')
-client.oauth.auth_url()
+client = foursquare.Foursquare(
+ client_id='YOUR_CLIENT_ID',
+ client_secret='YOUR_CLIENT_SECRET',
+ redirect_uri='http://localhost:8000'
+)
+print(client.oauth.auth_url())
```
-This will output something like:
-
- 'https://foursquare.com/oauth2/authenticate?client_id=YOUR_CLIENT_ID&response_type=code&redirect_uri=http%3A%2F%2Flocalhost%3A8000%2F'
-
-Copy the URL from your terminal *without the surrounding quotes* and paste it into a web browser.
-
-Your browser should redirect to a URL like the one below, with an error about not being able to connect to the server (unless you have a webserver running locally on your machine):
-
- http://localhost:8000/?code=XX_CODE_RETURNED_IN_REDIRECT_XX#_=_
-
-Copy the code represented by `XX_CODE_RETURNED_IN_REDIRECT_XX` (note that there may be an extra `#_=_` on the end which *you should not copy*).
-
-Back in your python shell, with that code, enter this, replacing
-`XX_CODE_RETURNED_IN_REDIRECT_XX` with the code you just copied:
+3. Visit the printed URL in your browser
+4. Copy the code from the redirect URL
+5. Get your token:
```python
-client.oauth.get_token('XX_CODE_RETURNED_IN_REDIRECT_XX')
+client.oauth.get_token('YOUR_CODE_HERE')
```
-This will output another long code, which is your Access Token.
+## Usage
-Enter this in your `config.ini`.
+### Generate an iCal file
-
-## Run the script
-
-Generate a `.ics` file:
-
- $ ./generate_feeds.py
-
-This should create an `.ics` file containing up to 250 of your most recent
-checkins (see `--all` argument below to get more).
-
-If the file is generated in a location on your website that's publicly-visible, you should be able to subscribe to it from a calendar application. Then run the script periodically to have it update.
-
-Note that the file might contain private checkins or information you don't want to be public. In which case, it's probably best to make the name of any such publicly-readable file very obscure.
-
-To generate a `.kml` file, see the `kind` option below.
-
-
-### Script options
-
-#### `--all`
-
-By default the script only fetches the most recent 250 checkins. To fetch ALL checkins add the `--all` flag:
+Create a `.ics` file with your recent check-ins:
```bash
-$ ./generate_feeds.py --all
+uv run ./generate_feeds.py
```
-Depending on how many checkins you have you might only want to run it with
-`--all` the first time and, once that's imported into a calendar application,
-subsequently only fetch recent checkins.
-
-### `-k` or `--kind`
-
-By default the script generates an iCal `.ics` file. Or, use this option to
-specify an `.ics` file or a `.kml` file:
+Get all your check-ins (may take a while):
```bash
-$ ./generate_feeds.py -k ics
-$ ./generate_feeds.py -k kml
-$ ./generate_feeds.py --kind=ics
-$ ./generate_feeds.py --kind=kml
+uv run ./generate_feeds.py --all
```
-#### `-v` or `--verbose`
+### Sync to CalDAV
-By default the script will only output text if something goes wrong. To get
-brief output use `-v` or `--verbose`:
+Upload check-ins directly to a CalDAV server:
```bash
-$ ./generate_feeds.py -v
-Fetched 250 checkins from the API
-Generated calendar file ./mycalendar.ics
+uv run ./generate_feeds.py --kind caldav
```
-If fetching `--all` checkins then increasing the verbosity with another `-v`
-will show more info than the above:
+Make sure your CalDAV settings are configured in `config.ini`.
+## Configuration
+
+The `config.ini` file supports these sections:
+
+### [Foursquare]
+- `AccessToken`: Your Foursquare API access token
+
+### [Local]
+- `IcsFilepath`: Path where the `.ics` file should be saved
+
+### [CalDAV]
+- `url`: Your CalDAV server URL
+- `username`: CalDAV username
+- `password`: CalDAV password
+- `calendar_name`: Name of the calendar to create/use
+
+## Command Line Options
+
+### `--all`
+Fetch all check-ins instead of just the recent 250:
```bash
-$ ./generate_feeds.py -vv --all
-5746 checkins to fetch
-Fetched checkins 1-250
-Fetched checkins 251-500
-[etc...]
-Fetched checkins 5501-5750
-Fetched 5744 checkins from the API
-Generated calendar file ./mycalendar.ics
+uv run ./generate_feeds.py --all
```
-(No I don't know why it fetched 2 fewer checkins than it says I have.)
+### `--kind` / `-k`
+Specify output type (`ics` or `caldav`):
+```bash
+uv run ./generate_feeds.py --kind ics
+uv run ./generate_feeds.py --kind caldav
+```
+### `--verbose` / `-v`
+Enable verbose output:
+```bash
+uv run ./generate_feeds.py -v # Basic info
+uv run ./generate_feeds.py -vv # Detailed progress (with --all)
+```
+
+## What Gets Exported
+
+Each check-in becomes a calendar event with:
+
+- **Title**: "@ [Venue Name]"
+- **Location**: Venue name and address
+- **Time**: 15-minute event starting at check-in time
+- **Description**: Your shout/comment, plus metadata like:
+ - Days since last visit
+ - Mayor status at the time
+- **URL**: Link to the check-in on Foursquare
+
+## Privacy Considerations
+
+- Check-ins may contain private information
+- If hosting `.ics` files publicly, use obscure filenames
+- Consider filtering private check-ins before sharing
## About
-By Phil Gyford
-phil@gyford.com
-https://www.gyford.com
-https://github.com/philgyford/foursquare-feeds
+**Original Author**: Phil Gyford
+**Repository**: https://github.com/philgyford/foursquare-feeds
+
+This tool exists because Foursquare's [official feeds](https://foursquare.com/feeds/) stopped working reliably. [Read more about the original motivation of Phil to create the tool](https://www.gyford.com/phil/writing/2019/05/13/foursquare-swarm-ical-feed/).
diff --git a/config_example.ini b/config_example.ini
index 727f8cc..cfe8c56 100644
--- a/config_example.ini
+++ b/config_example.ini
@@ -8,5 +8,4 @@ password=xxxxx
calendar_name=Swarm Check-Ins
[Local]
-IcsFilepath=./foursquare.ics
-KmlFilepath=./foursquare.kml
\ No newline at end of file
+IcsFilepath=./foursquare.ics
\ No newline at end of file
diff --git a/generate_feeds.py b/generate_feeds.py
index 063815d..f681c97 100755
--- a/generate_feeds.py
+++ b/generate_feeds.py
@@ -16,7 +16,7 @@ current_dir = os.path.realpath(os.path.dirname(__file__))
CONFIG_FILE = os.path.join(current_dir, "config.ini")
# The kinds of file we can generate:
-VALID_KINDS = ["ics", "kml", "caldav"]
+VALID_KINDS = ["ics", "caldav"]
class FeedGenerator:
@@ -44,7 +44,6 @@ class FeedGenerator:
self.api_access_token = config.get("Foursquare", "AccessToken")
self.ics_filepath = config.get("Local", "IcsFilepath")
- self.kml_filepath = config.get("Local", "KmlFilepath")
self.caldav_url = config.get("CalDAV", "url", fallback=None)
self.caldav_username = config.get("CalDAV", "username", fallback=None)
self.caldav_password = config.get("CalDAV", "password", fallback=None)
@@ -64,11 +63,12 @@ class FeedGenerator:
self.logger.info("Fetched {} checkin{} from the API".format(len(checkins), plural))
if kind == "ics":
- filepath = self._generate_ics_file(checkins)
- elif kind == "kml":
- filepath = self._generate_kml_file(checkins)
+ calendar = self._generate_calendar(checkins)
- self.logger.info("Generated file {}".format(filepath))
+ with open(self.ics_filepath, "w") as f:
+ f.writelines(calendar)
+
+ self.logger.info("Generated file {}".format(self.ics_filepath))
def _get_recent_checkins(self) -> list:
"Make one request to the API for the most recent checkins."
@@ -167,7 +167,7 @@ class FeedGenerator:
continue
venue_name = checkin["venue"]["name"]
- tz_offset = self._get_checkin_timezone(checkin)
+ tz_offset = tzoffset(None, checkin["timeZoneOffset"] * 60)
e = Event()
start = arrow.get(checkin["createdAt"]).replace(tzinfo=tz_offset)
@@ -209,91 +209,6 @@ class FeedGenerator:
return c
- def _generate_kml_file(self, checkins):
- """Supplied with a list of checkin data from the API, generates
- 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.
- """
- import simplekml
-
- user = self._get_user()
-
- kml = simplekml.Kml()
-
- # The original Foursquare files had a Folder with name and
- # description like this, so:
- names = [user.get("firstName", ""), user.get("lastName", "")]
- user_name = " ".join(names).strip()
- name = "foursquare checkin history for {}".format(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 = "https://foursquare.com/v/{}".format(checkin["venue"]["id"])
-
- description = ['@{}'.format(url, venue_name)]
- if "shout" in checkin and len(checkin["shout"]) > 0:
- description.append('"{}"'.format(checkin["shout"]))
- description.append("Timezone offset: {}".format(tz_offset))
-
- coords = [
- (
- checkin["venue"]["location"]["lng"],
- checkin["venue"]["location"]["lat"],
- )
- ]
-
- visibility = 0 if "private" in checkin else 1
-
- pnt = fol.newpoint(
- name=venue_name,
- description="".format("\n".join(description)),
- coords=coords,
- visibility=visibility,
- # 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 = arrow.get(
- checkin["createdAt"],
- tzinfo=self._get_checkin_timezone(checkin),
- ).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 an arrow timezone object
- representing the timezone offset of that checkin.
-
- Keyword arguments
- checkin -- A dict of data about a single checkin
- """
- return tzoffset(None, checkin["timeZoneOffset"] * 60)
-
def sync_calendar_to_caldav(self):
"""
Syncs all events from the generated calendar to a CalDAV server.
@@ -328,29 +243,9 @@ class FeedGenerator:
cal = principal.make_calendar(name=self.caldav_calendar_name)
self.logger.debug("Calendar has {} events".format(len(calendar.events)))
+
# Upload each event from the ics.Calendar object
for event in calendar.events:
- # Each event must have a unique UID
- # Use the event UID if present, otherwise generate a deterministic one from checkin ID
- if not event.uid:
- # Try to extract checkin ID from event.url or event.name as fallback
- checkin_id = None
- if hasattr(event, "url") and event.url:
- # URL format: .../checkin/
- parts = event.url.rstrip("/").split("/")
- if "checkin" in parts:
- idx = parts.index("checkin")
- if idx + 1 < len(parts):
- checkin_id = parts[idx + 1]
- if not checkin_id and hasattr(event, "uid") and event.uid:
- # fallback: try to parse from event.uid
- if "@" in event.uid:
- checkin_id = event.uid.split("@")[0]
- if not checkin_id:
- # fallback: use event.name
- checkin_id = event.name
- # Generate a repeatable UID using a namespace and checkin_id
- event.uid = "{}@foursquare.com".format(checkin_id)
self.logger.debug("Uploading event with UID: {}".format(event.uid))
cal.add_event(event.serialize())
@@ -375,7 +270,7 @@ def main():
"-k",
"--kind",
action="store",
- help="Either ics, kml, or caldav. Default is ics.",
+ help="Either ics, or caldav. Default is ics.",
choices=VALID_KINDS,
default="ics",
required=False,
@@ -411,8 +306,6 @@ def main():
# Generate the requested kind of file
generator.generate(kind=args.kind)
- return 0
-
if __name__ == "__main__":
import sys
- sys.exit(main())
\ No newline at end of file
+ sys.exit(main() or 0)
\ No newline at end of file
diff --git a/pyproject.toml b/pyproject.toml
index c482643..2633f57 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -8,5 +8,4 @@ dependencies = [
"caldav>=2.0.1",
"foursquare",
"ics",
- "simplekml",
]
diff --git a/requirements.txt b/requirements.txt
deleted file mode 100644
index 76077c0..0000000
--- a/requirements.txt
+++ /dev/null
@@ -1,76 +0,0 @@
-# This file was autogenerated by uv via the following command:
-# uv export --format requirements-txt --output-file requirements.txt
-arrow==1.3.0 \
- --hash=sha256:d4540617648cb5f895730f1ad8c82a65f2dad0166f57b75f3ca54759c4d67a85 \
- --hash=sha256:c728b120ebc00eb84e01882a6f5e7927a53960aa990ce7dd2b10f39005a67f80
-attrs==24.2.0 \
- --hash=sha256:5cfb1b9148b5b086569baec03f20d7b6bf3bcacc9a42bebf87ffaaca362f6346 \
- --hash=sha256:81921eb96de3191c8258c199618104dd27ac608d9366f5e35d011eae1867ede2
-certifi==2024.8.30 \
- --hash=sha256:bec941d2aa8195e248a60b31ff9f0558284cf01a52591ceda73ea9afffd69fd9 \
- --hash=sha256:922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8
-charset-normalizer==3.4.0 \
- --hash=sha256:223217c3d4f82c3ac5e29032b3f1c2eb0fb591b72161f86d93f5719079dae93e \
- --hash=sha256:0713f3adb9d03d49d365b70b84775d0a0d18e4ab08d12bc46baa6132ba78aaf6 \
- --hash=sha256:de7376c29d95d6719048c194a9cf1a1b0393fbe8488a22008610b0361d834ecf \
- --hash=sha256:4a51b48f42d9358460b78725283f04bddaf44a9358197b889657deba38f329db \
- --hash=sha256:b295729485b06c1a0683af02a9e42d2caa9db04a373dc38a6a58cdd1e8abddf1 \
- --hash=sha256:ee803480535c44e7f5ad00788526da7d85525cfefaf8acf8ab9a310000be4b03 \
- --hash=sha256:3d59d125ffbd6d552765510e3f31ed75ebac2c7470c7274195b9161a32350284 \
- --hash=sha256:8cda06946eac330cbe6598f77bb54e690b4ca93f593dee1568ad22b04f347c15 \
- --hash=sha256:07afec21bbbbf8a5cc3651aa96b980afe2526e7f048fdfb7f1014d84acc8b6d8 \
- --hash=sha256:6b40e8d38afe634559e398cc32b1472f376a4099c75fe6299ae607e404c033b2 \
- --hash=sha256:b8dcd239c743aa2f9c22ce674a145e0a25cb1566c495928440a181ca1ccf6719 \
- --hash=sha256:84450ba661fb96e9fd67629b93d2941c871ca86fc38d835d19d4225ff946a631 \
- --hash=sha256:44aeb140295a2f0659e113b31cfe92c9061622cadbc9e2a2f7b8ef6b1e29ef4b \
- --hash=sha256:1db4e7fefefd0f548d73e2e2e041f9df5c59e178b4c72fbac4cc6f535cfb1565 \
- --hash=sha256:5726cf76c982532c1863fb64d8c6dd0e4c90b6ece9feb06c9f202417a31f7dd7 \
- --hash=sha256:b197e7094f232959f8f20541ead1d9862ac5ebea1d58e9849c1bf979255dfac9 \
- --hash=sha256:dd4eda173a9fcccb5f2e2bd2a9f423d180194b1bf17cf59e3269899235b2a114 \
- --hash=sha256:e9e3c4c9e1ed40ea53acf11e2a386383c3304212c965773704e4603d589343ed \
- --hash=sha256:92a7e36b000bf022ef3dbb9c46bfe2d52c047d5e3f3343f43204263c5addc250 \
- --hash=sha256:54b6a92d009cbe2fb11054ba694bc9e284dad30a26757b1e372a1fdddaf21920 \
- --hash=sha256:1ffd9493de4c922f2a38c2bf62b831dcec90ac673ed1ca182fe11b4d8e9f2a64 \
- --hash=sha256:35c404d74c2926d0287fbd63ed5d27eb911eb9e4a3bb2c6d294f3cfd4a9e0c23 \
- --hash=sha256:4796efc4faf6b53a18e3d46343535caed491776a22af773f366534056c4e1fbc \
- --hash=sha256:e7fdd52961feb4c96507aa649550ec2a0d527c086d284749b2f582f2d40a2e0d \
- --hash=sha256:92db3c28b5b2a273346bebb24857fda45601aef6ae1c011c0a997106581e8a88 \
- --hash=sha256:ab973df98fc99ab39080bfb0eb3a925181454d7c3ac8a1e695fddfae696d9e90 \
- --hash=sha256:4b67fdab07fdd3c10bb21edab3cbfe8cf5696f453afce75d815d9d7223fbe88b \
- --hash=sha256:aa41e526a5d4a9dfcfbab0716c7e8a1b215abd3f3df5a45cf18a12721d31cb5d \
- --hash=sha256:ffc519621dce0c767e96b9c53f09c5d215578e10b02c285809f76509a3931482 \
- --hash=sha256:f19c1585933c82098c2a520f8ec1227f20e339e33aca8fa6f956f6691b784e67 \
- --hash=sha256:707b82d19e65c9bd28b81dde95249b07bf9f5b90ebe1ef17d9b57473f8a64b7b \
- --hash=sha256:fe9f97feb71aa9896b81973a7bbada8c49501dc73e58a10fcef6663af95e5079
-foursquare==1!2020.1.30 \
- --hash=sha256:a9f27c3c426783a18ab96b1872345c9617a5513e0896c0bb4243840fab80eed3 \
- --hash=sha256:a5aa9c5b6609ea2610e929288fead0cf27a8bcea2141e2ae7289f6feb57d2041
-ics==0.7.2 \
- --hash=sha256:6743539bca10391635249b87d74fcd1094af20b82098bebf7c7521df91209f05 \
- --hash=sha256:5fcf4d29ec6e7dfcb84120abd617bbba632eb77b097722b7df70e48dbcf26103
-idna==3.10 \
- --hash=sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9 \
- --hash=sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3
-python-dateutil==2.9.0.post0 \
- --hash=sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3 \
- --hash=sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427
-pytz==2024.2 \
- --hash=sha256:2aa355083c50a0f93fa581709deac0c9ad65cca8a9e9beac660adcbd493c798a \
- --hash=sha256:31c7c1817eb7fae7ca4b8c7ee50c72f93aa2dd863de768e1ef4245d426aa0725
-requests==2.32.3 \
- --hash=sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760 \
- --hash=sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6
-simplekml==1.3.6 \
- --hash=sha256:cda687be2754395fcab664e908ebf589facd41e8436d233d2be37a69efb1c536
-six==1.16.0 \
- --hash=sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926 \
- --hash=sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254
-tatsu==5.12.2 \
- --hash=sha256:5894dc7ddba9a1886a95ff2f06cef1be2b3d3a37c776eba8177ef4dcd80ccb03 \
- --hash=sha256:9c313186ae5262662cb3fbec52c9a12db1ef752e615f46cac3eb568cb91eacf9
-types-python-dateutil==2.9.0.20241003 \
- --hash=sha256:58cb85449b2a56d6684e41aeefb4c4280631246a0da1a719bdbe6f3fb0317446 \
- --hash=sha256:250e1d8e80e7bbc3a6c99b907762711d1a1cdd00e978ad39cb5940f6f0a87f3d
-urllib3==2.2.3 \
- --hash=sha256:e7d814a81dad81e6caf2ec9fdedb284ecc9c73076b62654547cc64ccdcae26e9 \
- --hash=sha256:ca899ca043dcb1bafa3e262d73aa25c465bfb49e0bd9dd5d59f1d0acba2f8fac
diff --git a/uv.lock b/uv.lock
index 7a849f2..563136d 100644
--- a/uv.lock
+++ b/uv.lock
@@ -123,13 +123,12 @@ wheels = [
[[package]]
name = "foursquare-feeds"
-version = "2.3.0"
+version = "2.3.1"
source = { virtual = "." }
dependencies = [
{ name = "caldav" },
{ name = "foursquare" },
{ name = "ics" },
- { name = "simplekml" },
]
[package.metadata]
@@ -137,7 +136,6 @@ requires-dist = [
{ name = "caldav", specifier = ">=2.0.1" },
{ name = "foursquare" },
{ name = "ics" },
- { name = "simplekml" },
]
[[package]]
@@ -260,12 +258,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/f9/9b/335f9764261e915ed497fcdeb11df5dfd6f7bf257d4a6a2a686d80da4d54/requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6", size = 64928, upload-time = "2024-05-29T15:37:47.027Z" },
]
-[[package]]
-name = "simplekml"
-version = "1.3.6"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/15/e4/c333a93b7e3346437ad1ff42b8e362b853eb405ad6243ab6163f9af2a460/simplekml-1.3.6.tar.gz", hash = "sha256:cda687be2754395fcab664e908ebf589facd41e8436d233d2be37a69efb1c536", size = 52999, upload-time = "2021-09-16T17:08:27.038Z" }
-
[[package]]
name = "six"
version = "1.16.0"