-
Notifications
You must be signed in to change notification settings - Fork 1
/
index.py
190 lines (153 loc) · 7.12 KB
/
index.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
import enum
import sys
import asyncio
import webbrowser
import code
from dataclasses import dataclass
from datetime import datetime
from pyodide.http import pyfetch
version_string = None # Version string variable is exported as a global variable. Value is assigned when the py-script main() is executed.
ProfileData = None # Class with descriptor attributes, for loading profile data. Initialised from main()
interpreter = code.InteractiveInterpreter() # runs user commands
def execute_command(command: str) -> str:
import io
from contextlib import redirect_stdout, redirect_stderr
# stdout and stderr are redirected to a string to be given as return to the js call
with io.StringIO() as buf, redirect_stderr(buf):
with io.StringIO() as buf1, redirect_stdout(buf1):
for field in profile_fields_list:
if field in command:
command = command.replace(
field, repr(getattr(ProfileData, field.lower()))
)
compiled_code = code.compile_command(
command
) # If compile code is not used, variable repr are not printed
interpreter.runcode(compiled_code)
output = buf1.getvalue()
output = output or buf.getvalue()
output = "\n\r".join(output.split("\n"))
return output
# Used to store the profile data, along with the time it was fetched
# This is used to re-fetch the data after a certain amount of time has passed (currently 5 minutes)
@dataclass
class ProfileDataRecord:
data_dict: dict
refreshed_timestamp: float
# Enum defining profile categories
class ProfileFields(enum.Enum):
NAME = enum.auto()
ABOUT = enum.auto()
SKILLS = enum.auto()
EXPERIENCE = enum.auto()
CONFERENCE_TALKS = enum.auto()
OPEN_SOURCE_CONTRIBUTIONS = enum.auto()
EDUCATION = enum.auto()
CERTIFICATIONS = enum.auto()
profile_fields_list = [
member.name for member in ProfileFields
] # Used to show the pre-loaded variables
# Descriptor class for fetching profile data. Used to make variable loading dynamic
class ProfileFetchDescriptor:
profile_data: ProfileDataRecord | None = None
@classmethod
async def load_profile_data(cls) -> None:
# Get profile data by calling gitconnected API and store it as a class attribute
resp = await pyfetch("https://gitconnected.com/v1/portfolio/adarshdigievo")
cls.profile_data = ProfileDataRecord(
data_dict=await resp.json(), refreshed_timestamp=datetime.now().timestamp()
)
def __init__(self, field: ProfileFields) -> None:
self.field = field
def __get__(self, _, __) -> str:
current_timestamp = datetime.now().timestamp()
if (
current_timestamp - ProfileFetchDescriptor.profile_data.refreshed_timestamp
> 300
):
asyncio.run(self.load_profile_data())
match self.field:
case ProfileFields.NAME:
return ProfileFetchDescriptor.profile_data.data_dict["basics"]["name"]
case ProfileFields.ABOUT:
about = ""
about += (
f"{ProfileFetchDescriptor.profile_data.data_dict['basics']['name']} \n{ProfileFetchDescriptor.profile_data.data_dict['basics']['label']} \n"
""
)
about += f"{ProfileFetchDescriptor.profile_data.data_dict['basics']['summary']}\n"
about += "\nSocial Profiles: \n"
for profile_data in ProfileFetchDescriptor.profile_data.data_dict[
"basics"
]["profiles"]:
about += f"{profile_data['network']}: {profile_data['username']}\n{profile_data['url']} \n\n"
return str(about)
case ProfileFields.SKILLS:
skill_list = ProfileFetchDescriptor.profile_data.data_dict["skills"]
return "\n".join(
f"{skill['name']} {'☆ ' * skill['rating']}" for skill in skill_list
)
case ProfileFields.EXPERIENCE:
exp = ""
for exp_data in ProfileFetchDescriptor.profile_data.data_dict["work"]:
exp += f"\n{'-' * 30}\n{exp_data['position']} at {exp_data['name']} | {exp_data['startDate']} - {exp_data['endDate']} | {exp_data['website']} \n\n"
exp += f"{exp_data['summary']} \n"
if exp_data["highlights"]:
exp += "\n".join(
f"- {highlight}" for highlight in exp_data["highlights"]
)
exp += "\n\n"
return exp
case ProfileFields.CONFERENCE_TALKS:
talks = ""
for talk in ProfileFetchDescriptor.profile_data.data_dict[
"publications"
]:
talks += f"\n{talk['name']}\n"
talks += f"{talk['url']} \n{talk['summary']} \n"
return talks
case ProfileFields.OPEN_SOURCE_CONTRIBUTIONS:
oss_contributions = ""
for contrib in ProfileFetchDescriptor.profile_data.data_dict[
"projects"
]:
oss_contributions += f"\n☆ {contrib['name']}\n"
oss_contributions += (
f"{contrib['githubUrl']} \n{contrib['summary']} \n"
)
return oss_contributions
case ProfileFields.EDUCATION:
education = ""
for edu in ProfileFetchDescriptor.profile_data.data_dict["education"]:
education += f"\n{edu['institution']} | {edu['area']} | {edu['studyType']} | {edu['startDate']} - {edu['endDate']} \n"
education += f"{edu['description']} \n"
return education
case ProfileFields.CERTIFICATIONS:
return "".join(
f"\n{cert['name']} | {cert['issuer']} \n"
for cert in ProfileFetchDescriptor.profile_data.data_dict[
"certificates"
]
)
case _:
return "WIP"
async def main():
await ProfileFetchDescriptor.load_profile_data()
attrs = {
field.name.lower(): ProfileFetchDescriptor(field) for field in ProfileFields
}
global ProfileData
# Dynamically generate a class named ProfileData. Each member attribute of the class is a descriptor
ProfileData = type("ProfileData", (), attrs)
global site_description_string # making it global, so that it can be accessed from js, via pyodide interpreter globals
site_description_string = "Adarsh Divakaran | Portfolio REPL\n\n\r"
site_description_string += (
f'Pre-loaded variables: {", ".join(profile_fields_list)}.\n\r'
)
site_description_string += f"Try entering `print(ABOUT)` in the REPL\n\n\r"
global version_string
version_string = f"Python {sys.version}\n\r"
version_string += (
'Type "help", "copyright", "credits" or "license" for more information.\n\r'
)
asyncio.ensure_future(main())