Skip to content

Conversation

Iron0utlaw
Copy link

@Iron0utlaw Iron0utlaw commented Dec 26, 2023

Description

This script command is designed to incorporate upcoming Codeforces contests into Google Calendar. It leverages the Codeforces public API to retrieve information about the upcoming contests and utilizes the Google Calendar API to add them. To keep track of previously added contests, the script utilizes a data.txt file to store the corresponding contest IDs.

Type of change

  • New script command

Screenshot

Screenshot 2023-12-26 at 13 46 45

Dependencies / Requirements

Dependencies

  • google-api-python-client
  • google-auth-httplib2
  • google-auth-oauthlib
  • requests

API Credentials

Checklist

@unnamedd @zzamboni It would be great if you guys could review this PR

@unnamedd unnamedd self-requested a review January 8, 2024 12:17
@unnamedd
Copy link
Contributor

unnamedd commented Jan 8, 2024

Hey @Iron0utlaw,
sure thing, will be a pleasure.
Sorry for the long delay to answer, I will review it as soon as possible.

Copy link
Member

@dehesa dehesa left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey there,

Sorry it took so long to review. I was hoping @unnamedd would lend a hand here since my Python knowledge is fairly low 😛

I left below some comments; aside from that and as far as I understand, this script would generate a further 3 files: token.json, creds.json, and data.txt. I am not a big fan of that. Do you have any suggestion @unnamedd on how to not have so many lingering files?


# Required parameters:
# @raycast.schemaVersion 1
# @raycast.title Add Codeforces Contests
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The @raycast.title and @raycast.packageName will appear together in the RootSearch. Therefor I suggest you short this up to:

# @raycast.title Add Contests

'description': 'Codeforces Contest',
'start': {
'dateTime': start_time,
'timeZone': 'Asia/Kolkata',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is hardcoded to Kolkata in the middle of the code, it would be better to extract the timezone in a variable at the beginning of the code so it is easy to change for users.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1

Copy link
Contributor

@unnamedd unnamedd left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @Iron0utlaw, 👋
thanks for contributing to our repository.

I wrote some comments for you.
Please, read them carefully because there are too many things we need to discuss before moving on (or not).

As soon as you finish to address them, please re-request a review for a second round.

# @raycast.packageName Codeforces

# Documentation:
# @raycast.description Adds codeforces contests to Google Calendar
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
# @raycast.description Adds codeforces contests to Google Calendar
# @raycast.description Adds Codeforces contests to Google Calendar


# Documentation:
# @raycast.description Adds codeforces contests to Google Calendar
# @raycast.author Harsh Varshney
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
# @raycast.author Harsh Varshney
# @raycast.authorURL https://github.com/Iron0utlaw

@@ -0,0 +1,117 @@
#!/usr/bin/env python3

# Install dependencies using "pip install --upgrade google-api-python-client google-auth-httplib2 google-auth-oauthlib requests"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are a few things I would like to raise here:

  1. The setup for this Script Command is not straightforward, which means that would be great if we could add the link to the Google Quickstart in order to help your user, like Google did
image

As far as I could see, it is not possible to not have the credentials directly here in the code itself, they need to read from credentials.json because they will rewrite the file after finishing the authorisation process.

  1. The dependencies needed to use the Script Command, you must keep them as minimum as you can.

  2. Personally speaking, I'd rather to have a README file with instructions of what need to be done and installed, and also, I'd create a requirements.txt with all the dependencies needed to your Script Command (you can do that just running pip3 freeze > requirements.txt or just copying clicking in the link)

  3. Even though with all of that, worth to mention that Raycast run the Script Commands in a non-login environment, which means that, even with your user installing the dependencies they may not run your Script Command properly, not because of your implementation, but because of Raycast itself.

If you check my screenshot, I couldn't import requests (one of your Python dependencies) because where the Script Command is running, requests wasn't installed.

image

Which lead me to a (stupid, sorry) question: have you ever tested your Script Command on Raycast or just using the Terminal?

import os.path
import time
import json
from pip._vendor import requests
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am quite sure import requests is more than enough.

'description': 'Codeforces Contest',
'start': {
'dateTime': start_time,
'timeZone': 'Asia/Kolkata',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1

Comment on lines +100 to +117
now = time.time()
LINK = "https://codeforces.com/api/contest.list?gym=false"

res = requests.get(LINK)
resjson = res.json()

data = load_set_from_file('data.txt')

for con in resjson["result"]:
if con["relativeTimeSeconds"] > 0:
break
if con["id"] not in data:
add_event(con)
data.add(con["id"])

save_set_to_file('data.txt',data)

print("Success")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would be awesome if you convert that piece of code from the root of the file into a function.

Suggested change
now = time.time()
LINK = "https://codeforces.com/api/contest.list?gym=false"
res = requests.get(LINK)
resjson = res.json()
data = load_set_from_file('data.txt')
for con in resjson["result"]:
if con["relativeTimeSeconds"] > 0:
break
if con["id"] not in data:
add_event(con)
data.add(con["id"])
save_set_to_file('data.txt',data)
print("Success")
def load_content():
now = time.time()
LINK = "https://codeforces.com/api/contest.list?gym=false"
res = requests.get(LINK)
resjson = res.json()
data = load_set_from_file('data.txt')
for con in resjson["result"]:
if con["relativeTimeSeconds"] > 0:
break
if con["id"] not in data:
add_event(con)
data.add(con["id"])
save_set_to_file('data.txt',data)
print("Success")
if __name__ == "__main__":
load_content()

Comment on lines +93 to +95
def jprint(obj):
text = json.dumps(obj, sort_keys=True, indent=4)
print(text)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not being used anywhere.

Suggested change
def jprint(obj):
text = json.dumps(obj, sort_keys=True, indent=4)
print(text)

data = json.load(file)
return set(data)
except FileNotFoundError:
return set()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As part of the process of informing your user about something is missing, would be great if you print in the exception which file is missing instead of just returning a set(), or (as you are only using to load data.txt, you may create the file instead of showing an error.

Comment on lines +36 to +49
if os.path.exists("token.json"):
creds = Credentials.from_authorized_user_file("token.json", SCOPES)

if not creds or not creds.valid:
if creds and creds.expired and creds.refresh_token:
creds.refresh(Request())
else:
flow = InstalledAppFlow.from_client_secrets_file(
"creds.json", SCOPES
)
creds = flow.run_local_server(port=0)

with open("token.json", "w") as token:
token.write(creds.to_json())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is not good to run the authorisation process every single time you call add_event.
You must extract that process from here into another method and call it before calling add_event, because this method is being called inside of a for-loop.

@dehesa
Copy link
Member

dehesa commented Jan 19, 2024

Hey @Iron0utlaw,

Do you intend to work on this PR?

@Iron0utlaw
Copy link
Author

Yes, I will start working on this

@Iron0utlaw Iron0utlaw closed this Jan 23, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants