mirror of
https://github.com/nikdoof/foursquare-feeds.git
synced 2025-12-11 16:02:19 +00:00
Remove KML output support
This commit is contained in:
@@ -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
|
||||
|
||||
|
||||
235
README.md
235
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/).
|
||||
|
||||
@@ -8,5 +8,4 @@ password=xxxxx
|
||||
calendar_name=Swarm Check-Ins
|
||||
|
||||
[Local]
|
||||
IcsFilepath=./foursquare.ics
|
||||
KmlFilepath=./foursquare.kml
|
||||
IcsFilepath=./foursquare.ics
|
||||
@@ -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 = ['@<a href="{}">{}</a>'.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="<![CDATA[{}]]>".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/<checkin_id>
|
||||
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())
|
||||
sys.exit(main() or 0)
|
||||
@@ -8,5 +8,4 @@ dependencies = [
|
||||
"caldav>=2.0.1",
|
||||
"foursquare",
|
||||
"ics",
|
||||
"simplekml",
|
||||
]
|
||||
|
||||
@@ -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
|
||||
10
uv.lock
generated
10
uv.lock
generated
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user