making-with-code-cli 5.0.3__tar.gz → 5.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {making_with_code_cli-5.0.3 → making_with_code_cli-5.2.0}/PKG-INFO +1 -1
- {making_with_code_cli-5.0.3 → making_with_code_cli-5.2.0}/pyproject.toml +1 -1
- {making_with_code_cli-5.0.3 → making_with_code_cli-5.2.0}/src/making_with_code_cli/curriculum.py +9 -3
- {making_with_code_cli-5.0.3 → making_with_code_cli-5.2.0}/src/making_with_code_cli/git_backend/mwc_backend.py +31 -7
- making_with_code_cli-5.2.0/src/making_with_code_cli/helpers.py +38 -0
- {making_with_code_cli-5.0.3 → making_with_code_cli-5.2.0}/src/making_with_code_cli/mwc_accounts_api.py +19 -1
- {making_with_code_cli-5.0.3 → making_with_code_cli-5.2.0}/src/making_with_code_cli/settings.py +2 -2
- {making_with_code_cli-5.0.3 → making_with_code_cli-5.2.0}/src/making_with_code_cli/setup/__init__.py +7 -9
- {making_with_code_cli-5.0.3 → making_with_code_cli-5.2.0}/src/making_with_code_cli/setup/tasks.py +43 -62
- {making_with_code_cli-5.0.3 → making_with_code_cli-5.2.0}/src/making_with_code_cli/teach/section/__init__.py +13 -4
- making_with_code_cli-5.2.0/src/making_with_code_cli/teach/section/delete.py +24 -0
- {making_with_code_cli-5.0.3 → making_with_code_cli-5.2.0}/src/making_with_code_cli/teach/section/edit.py +8 -2
- {making_with_code_cli-5.0.3 → making_with_code_cli-5.2.0}/src/making_with_code_cli/teach/section/show.py +7 -2
- {making_with_code_cli-5.0.3 → making_with_code_cli-5.2.0}/src/making_with_code_cli/teach/setup.py +2 -8
- making_with_code_cli-5.2.0/src/making_with_code_cli/teach/student/__init__.py +12 -0
- making_with_code_cli-5.0.3/src/making_with_code_cli/teach/student/update.py → making_with_code_cli-5.2.0/src/making_with_code_cli/teach/student/edit.py +9 -4
- making_with_code_cli-5.2.0/src/making_with_code_cli/teach/student/remove.py +34 -0
- {making_with_code_cli-5.0.3 → making_with_code_cli-5.2.0}/src/making_with_code_cli/update/__init__.py +1 -1
- making_with_code_cli-5.0.3/src/making_with_code_cli/helpers.py +0 -22
- making_with_code_cli-5.0.3/src/making_with_code_cli/teach/student/__init__.py +0 -10
- {making_with_code_cli-5.0.3 → making_with_code_cli-5.2.0}/README.md +0 -0
- {making_with_code_cli-5.0.3 → making_with_code_cli-5.2.0}/src/making_with_code_cli/__init__.py +0 -0
- {making_with_code_cli-5.0.3 → making_with_code_cli-5.2.0}/src/making_with_code_cli/cli.py +0 -0
- {making_with_code_cli-5.0.3 → making_with_code_cli-5.2.0}/src/making_with_code_cli/decorators.py +0 -0
- {making_with_code_cli-5.0.3 → making_with_code_cli-5.2.0}/src/making_with_code_cli/errors.py +0 -0
- {making_with_code_cli-5.0.3 → making_with_code_cli-5.2.0}/src/making_with_code_cli/git_backend/__init__.py +0 -0
- {making_with_code_cli-5.0.3 → making_with_code_cli-5.2.0}/src/making_with_code_cli/git_backend/base_backend.py +0 -0
- {making_with_code_cli-5.0.3 → making_with_code_cli-5.2.0}/src/making_with_code_cli/git_wrapper.py +0 -0
- {making_with_code_cli-5.0.3 → making_with_code_cli-5.2.0}/src/making_with_code_cli/styles.py +0 -0
- {making_with_code_cli-5.0.3 → making_with_code_cli-5.2.0}/src/making_with_code_cli/submit.py +0 -0
- {making_with_code_cli-5.0.3 → making_with_code_cli-5.2.0}/src/making_with_code_cli/teach/__init__.py +0 -0
- {making_with_code_cli-5.0.3 → making_with_code_cli-5.2.0}/src/making_with_code_cli/teach/assess.py +0 -0
- {making_with_code_cli-5.0.3 → making_with_code_cli-5.2.0}/src/making_with_code_cli/teach/check/__init__.py +0 -0
- {making_with_code_cli-5.0.3 → making_with_code_cli-5.2.0}/src/making_with_code_cli/teach/check/check_module.py +0 -0
- {making_with_code_cli-5.0.3 → making_with_code_cli-5.2.0}/src/making_with_code_cli/teach/gitea_api/api.py +0 -0
- {making_with_code_cli-5.0.3 → making_with_code_cli-5.2.0}/src/making_with_code_cli/teach/gitea_api/exceptions.py +0 -0
- {making_with_code_cli-5.0.3 → making_with_code_cli-5.2.0}/src/making_with_code_cli/teach/log.py +0 -0
- {making_with_code_cli-5.0.3 → making_with_code_cli-5.2.0}/src/making_with_code_cli/teach/patch.py +0 -0
- {making_with_code_cli-5.0.3 → making_with_code_cli-5.2.0}/src/making_with_code_cli/teach/section/create.py +0 -0
- {making_with_code_cli-5.0.3 → making_with_code_cli-5.2.0}/src/making_with_code_cli/teach/status.py +0 -0
- {making_with_code_cli-5.0.3 → making_with_code_cli-5.2.0}/src/making_with_code_cli/teach/student/create.py +0 -0
- {making_with_code_cli-5.0.3 → making_with_code_cli-5.2.0}/src/making_with_code_cli/teach/student_repo_functions.py +0 -0
- {making_with_code_cli-5.0.3 → making_with_code_cli-5.2.0}/src/making_with_code_cli/teach/student_repos.py +0 -0
- {making_with_code_cli-5.0.3 → making_with_code_cli-5.2.0}/src/making_with_code_cli/teach/update.py +0 -0
- {making_with_code_cli-5.0.3 → making_with_code_cli-5.2.0}/src/making_with_code_cli/version.py +0 -0
{making_with_code_cli-5.0.3 → making_with_code_cli-5.2.0}/src/making_with_code_cli/curriculum.py
RENAMED
|
@@ -12,10 +12,16 @@ def get_curriculum(mwc_site_url, course_name=None):
|
|
|
12
12
|
Returns the curriculum metadata for course_name.
|
|
13
13
|
"""
|
|
14
14
|
url = mwc_site_url + "/manifest"
|
|
15
|
-
|
|
15
|
+
try:
|
|
16
|
+
response = requests.get(url, timeout=15)
|
|
17
|
+
except requests.exceptions.RequestException as e:
|
|
18
|
+
raise CurriculumSiteNotAvailable(mwc_site_url) from e
|
|
16
19
|
if response.ok:
|
|
17
|
-
|
|
18
|
-
|
|
20
|
+
try:
|
|
21
|
+
text = response.text.strip(LIVE_RELOAD)
|
|
22
|
+
metadata = json.loads(text)
|
|
23
|
+
except (json.JSONDecodeError, ValueError) as e:
|
|
24
|
+
raise CurriculumSiteNotAvailable(mwc_site_url) from e
|
|
19
25
|
if course_name:
|
|
20
26
|
for course in metadata["courses"]:
|
|
21
27
|
if course["name"] == course_name:
|
|
@@ -20,10 +20,17 @@ from making_with_code_cli.errors import (
|
|
|
20
20
|
GitServerNotAvailable,
|
|
21
21
|
)
|
|
22
22
|
|
|
23
|
+
EDITOR_COMMANDS = {
|
|
24
|
+
"atom": "atom --wait",
|
|
25
|
+
"code": "code --wait",
|
|
26
|
+
"subl": "subl -n -w",
|
|
27
|
+
"mate": "mate -w",
|
|
28
|
+
"vim": "vim",
|
|
29
|
+
"emacs": "emacs",
|
|
30
|
+
}
|
|
31
|
+
|
|
23
32
|
class MWCBackend(GitBackend):
|
|
24
33
|
"""A Github backend. Students own their own repos and grant teachers access via token.
|
|
25
|
-
Note that this gives the teacher account access to the student's entire github account,
|
|
26
|
-
within scope.
|
|
27
34
|
"""
|
|
28
35
|
|
|
29
36
|
MWC_GIT_PROTOCOL = "https"
|
|
@@ -31,7 +38,7 @@ class MWCBackend(GitBackend):
|
|
|
31
38
|
COMMIT_TEMPLATE = ".commit_template"
|
|
32
39
|
|
|
33
40
|
def init_module(self, module, modpath):
|
|
34
|
-
"""Creates the named repo from a template.
|
|
41
|
+
"""Creates the named repo from a template.
|
|
35
42
|
"""
|
|
36
43
|
self.check_settings()
|
|
37
44
|
server, repo_owner, repo_name = self.parse_repo_url(module["repo_url"])
|
|
@@ -42,19 +49,36 @@ class MWCBackend(GitBackend):
|
|
|
42
49
|
self.create_from_template(repo_owner, repo_name)
|
|
43
50
|
with cd(modpath.parent):
|
|
44
51
|
self.clone_repo(repo_name)
|
|
52
|
+
self.configure_git(modpath)
|
|
45
53
|
if (modpath / self.COMMIT_TEMPLATE).exists():
|
|
46
|
-
run(
|
|
54
|
+
run(["git", "config", "--local", "commit.template", self.COMMIT_TEMPLATE],
|
|
47
55
|
check=True, cwd=modpath)
|
|
48
56
|
run("uv venv", shell=True, check=True, cwd=modpath, capture_output=True)
|
|
49
57
|
self.init_direnv(modpath)
|
|
50
58
|
|
|
59
|
+
def configure_git(self, modpath):
|
|
60
|
+
"""Configures git user settings locally for the repo."""
|
|
61
|
+
name = self.settings['mwc_username']
|
|
62
|
+
run(["git", "config", "--local", "user.name", name], check=True, cwd=modpath)
|
|
63
|
+
run(["git", "config", "--local", "user.email", "nobody@makingwithcode.org"],
|
|
64
|
+
check=True, cwd=modpath)
|
|
65
|
+
editor = self.settings.get('editor')
|
|
66
|
+
if editor in EDITOR_COMMANDS:
|
|
67
|
+
run(["git", "config", "--local", "core.editor", EDITOR_COMMANDS[editor]],
|
|
68
|
+
check=True, cwd=modpath)
|
|
69
|
+
|
|
51
70
|
def update(self, module, modpath, install=True):
|
|
52
71
|
if (modpath / ".git").is_dir():
|
|
53
72
|
with cd(modpath):
|
|
54
73
|
relpath = self.relative_path(modpath)
|
|
55
74
|
click.echo(address(f"Checking {relpath} for updates.", preformatted=True))
|
|
56
|
-
gitresult = run("git pull", shell=True,
|
|
57
|
-
|
|
75
|
+
gitresult = run("git pull", shell=True, capture_output=True, text=True)
|
|
76
|
+
if gitresult.returncode != 0:
|
|
77
|
+
click.echo(error(
|
|
78
|
+
f"Could not update {relpath}: {gitresult.stderr.strip()}\n"
|
|
79
|
+
"You may have local changes that conflict. Ask a teacher for help."
|
|
80
|
+
))
|
|
81
|
+
return
|
|
58
82
|
click.echo(info(gitresult.stdout))
|
|
59
83
|
if install and Path("pyproject.toml").exists():
|
|
60
84
|
result = run("uv sync", shell=True, capture_output=True, text=True, cwd=modpath)
|
|
@@ -92,7 +116,7 @@ class MWCBackend(GitBackend):
|
|
|
92
116
|
user = self.settings['mwc_username']
|
|
93
117
|
auth = user + ':' + self.settings['mwc_git_token']
|
|
94
118
|
url = f"{self.MWC_GIT_PROTOCOL}://{auth}@{self.MWC_GIT_SERVER}/{user}/{repo_name}.git"
|
|
95
|
-
run(
|
|
119
|
+
run(["git", "clone", url], check=True, capture_output=True)
|
|
96
120
|
|
|
97
121
|
def check_settings(self):
|
|
98
122
|
if "mwc_username" not in self.settings:
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
from contextlib import contextmanager
|
|
3
|
+
import dateparser
|
|
4
|
+
import os
|
|
5
|
+
import csv
|
|
6
|
+
from making_with_code_cli.errors import MWCError
|
|
7
|
+
|
|
8
|
+
@contextmanager
|
|
9
|
+
def cd(path):
|
|
10
|
+
"""Sets the cwd within the context
|
|
11
|
+
"""
|
|
12
|
+
origin = Path().resolve()
|
|
13
|
+
try:
|
|
14
|
+
os.chdir(path)
|
|
15
|
+
yield
|
|
16
|
+
finally:
|
|
17
|
+
os.chdir(origin)
|
|
18
|
+
|
|
19
|
+
def in_bounds(value, minimum=None, maximum=None):
|
|
20
|
+
return (not minimum or value >= minimum) and (not maximum or value <= maximum)
|
|
21
|
+
|
|
22
|
+
def date_string(arg):
|
|
23
|
+
return dateparser.parse(arg, settings={'RETURN_AS_TIMEZONE_AWARE': True})
|
|
24
|
+
|
|
25
|
+
def read_roster_file(filepath):
|
|
26
|
+
try:
|
|
27
|
+
with open(filepath) as fh:
|
|
28
|
+
records = [record for record in csv.DictReader(fh)]
|
|
29
|
+
except Exception as err:
|
|
30
|
+
raise MWCError(f"Error reading roster file {filepath} as csv")
|
|
31
|
+
for i, record in enumerate(records):
|
|
32
|
+
if "username" not in record.keys():
|
|
33
|
+
raise MWCError(f"Error reading roster data from {filepath}: 'username' key missing in row {i}")
|
|
34
|
+
if "username" not in record.keys():
|
|
35
|
+
raise MWCError(f"Error reading roster data from {filepath}: 'password' key missing in row {i}")
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import click
|
|
1
2
|
import requests
|
|
2
3
|
import json
|
|
3
4
|
import os
|
|
@@ -5,12 +6,14 @@ from pathlib import Path
|
|
|
5
6
|
from urllib.parse import urljoin
|
|
6
7
|
from getpass import getpass
|
|
7
8
|
from making_with_code_cli.errors import MWCError
|
|
9
|
+
from making_with_code_cli.styles import debug as debug_fmt
|
|
8
10
|
|
|
9
11
|
MWC_ACCOUNTS_SERVER = "https://accounts.makingwithcode.org"
|
|
10
12
|
|
|
11
13
|
class MWCAccountsAPI:
|
|
12
|
-
def __init__(self, mwc_accounts_server=None):
|
|
14
|
+
def __init__(self, mwc_accounts_server=None, debug=False):
|
|
13
15
|
self.mwc_accounts_server = mwc_accounts_server or MWC_ACCOUNTS_SERVER
|
|
16
|
+
self.debug = debug
|
|
14
17
|
|
|
15
18
|
def login(self, username, password):
|
|
16
19
|
"Authenticates with a username and password, returning an auth token"
|
|
@@ -34,6 +37,10 @@ class MWCAccountsAPI:
|
|
|
34
37
|
response = self.post("/students", data=params, token=token)
|
|
35
38
|
return self.handle_response(response)
|
|
36
39
|
|
|
40
|
+
def remove_student_from_section(self, token, params):
|
|
41
|
+
response = self.delete("/students", data=params, token=token)
|
|
42
|
+
return self.handle_response(response)
|
|
43
|
+
|
|
37
44
|
def update_student(self, token, params):
|
|
38
45
|
response = self.put("/students", data=params, token=token)
|
|
39
46
|
return self.handle_response(response)
|
|
@@ -46,6 +53,10 @@ class MWCAccountsAPI:
|
|
|
46
53
|
response = self.put("/sections", data=params, token=token)
|
|
47
54
|
return self.handle_response(response)
|
|
48
55
|
|
|
56
|
+
def delete_section(self, token, params):
|
|
57
|
+
response = self.delete("/sections", data=params, token=token)
|
|
58
|
+
return self.handle_response(response)
|
|
59
|
+
|
|
49
60
|
def get(self, url, data=None, token=None):
|
|
50
61
|
return self.http_request("get", url, data=data, token=token)
|
|
51
62
|
|
|
@@ -55,9 +66,16 @@ class MWCAccountsAPI:
|
|
|
55
66
|
def put(self, url, data=None, token=None):
|
|
56
67
|
return self.http_request("put", url, data=data, token=token)
|
|
57
68
|
|
|
69
|
+
def delete(self, url, data=None, token=None):
|
|
70
|
+
return self.http_request("delete", url, data=data, token=token)
|
|
71
|
+
|
|
58
72
|
def http_request(self, method, url, data=None, token=None):
|
|
59
73
|
fn = getattr(requests, method)
|
|
60
74
|
headers = {"Authorization": f"Token {token}"} if token else None
|
|
75
|
+
if self.debug:
|
|
76
|
+
click.echo(debug_fmt(
|
|
77
|
+
f"{method} {self.mwc_accounts_server + url}, data={data}"
|
|
78
|
+
))
|
|
61
79
|
try:
|
|
62
80
|
return fn(self.mwc_accounts_server + url, data=data, headers=headers)
|
|
63
81
|
except requests.exceptions.ConnectionError:
|
{making_with_code_cli-5.0.3 → making_with_code_cli-5.2.0}/src/making_with_code_cli/settings.py
RENAMED
|
@@ -14,7 +14,7 @@ def get_settings_path(settings_path=None):
|
|
|
14
14
|
elif "MWC_CONFIG" in os.environ:
|
|
15
15
|
return Path(os.environ["MWC_CONFIG"])
|
|
16
16
|
else:
|
|
17
|
-
xdg_config_home =
|
|
17
|
+
xdg_config_home = os.environ.get("XDG_CONFIG_HOME", str(Path.home() / ".config"))
|
|
18
18
|
return xdg_config_home / "mwc" / "settings.toml"
|
|
19
19
|
|
|
20
20
|
def read_settings(settings_path=None):
|
|
@@ -35,7 +35,7 @@ def iter_settings(settings, prefix=None):
|
|
|
35
35
|
for key, value in settings.items():
|
|
36
36
|
keypath = (prefix or []) + [key]
|
|
37
37
|
if isinstance(value, dict):
|
|
38
|
-
for k, v in
|
|
38
|
+
for k, v in iter_settings(value, prefix=keypath):
|
|
39
39
|
yield '.'.join(keypath), v
|
|
40
40
|
else:
|
|
41
41
|
yield '.'.join(keypath), value
|
{making_with_code_cli-5.0.3 → making_with_code_cli-5.2.0}/src/making_with_code_cli/setup/__init__.py
RENAMED
|
@@ -40,18 +40,18 @@ from making_with_code_cli.setup.tasks import (
|
|
|
40
40
|
InstallImageMagick,
|
|
41
41
|
InstallHttpie,
|
|
42
42
|
InstallScipy,
|
|
43
|
-
|
|
43
|
+
InstallOllama,
|
|
44
|
+
InstallPandoc,
|
|
45
|
+
InstallPoppler,
|
|
44
46
|
)
|
|
45
47
|
|
|
46
48
|
@click.command()
|
|
47
49
|
@click.option("--config", help="Path to config file (default: ~/.mwc)")
|
|
48
50
|
@click.option("--debug", is_flag=True, help="Show debug-level output")
|
|
49
|
-
@click.option("--git-name", help="Set git name")
|
|
50
|
-
@click.option("--git-email", help="Set git email address")
|
|
51
51
|
@click.option("--mwc-accounts-url", help="Set URL for MWC accounts server")
|
|
52
52
|
@click.pass_context
|
|
53
53
|
@handle_mwc_errors
|
|
54
|
-
def setup(ctx, config, debug,
|
|
54
|
+
def setup(ctx, config, debug, mwc_accounts_url):
|
|
55
55
|
"""Set up the MWC command line interface"""
|
|
56
56
|
settings = read_settings(config)
|
|
57
57
|
sp = get_settings_path(config)
|
|
@@ -68,10 +68,6 @@ def setup(ctx, config, debug, git_name, git_email, mwc_accounts_url):
|
|
|
68
68
|
for note in INTRO_NOTES:
|
|
69
69
|
click.echo(address(note, list_format=True))
|
|
70
70
|
click.echo()
|
|
71
|
-
if git_name:
|
|
72
|
-
settings['git_name'] = git_name
|
|
73
|
-
if git_email:
|
|
74
|
-
settings['git_email'] = git_email
|
|
75
71
|
if mwc_accounts_url:
|
|
76
72
|
settings['mwc_accounts_url'] = mwc_accounts_url
|
|
77
73
|
settings['mwc_username'] = choose_mwc_username(settings.get("mwc_username"))
|
|
@@ -112,7 +108,9 @@ def setup(ctx, config, debug, git_name, git_email, mwc_accounts_url):
|
|
|
112
108
|
InstallVSCode,
|
|
113
109
|
InstallImageMagick,
|
|
114
110
|
InstallHttpie,
|
|
115
|
-
|
|
111
|
+
InstallOllama,
|
|
112
|
+
InstallPandoc,
|
|
113
|
+
InstallPoppler,
|
|
116
114
|
]
|
|
117
115
|
errors = []
|
|
118
116
|
for task_class in task_classes:
|
{making_with_code_cli-5.0.3 → making_with_code_cli-5.2.0}/src/making_with_code_cli/setup/tasks.py
RENAMED
|
@@ -28,6 +28,7 @@ from making_with_code_cli.errors import (
|
|
|
28
28
|
)
|
|
29
29
|
import click
|
|
30
30
|
import requests
|
|
31
|
+
import shutil
|
|
31
32
|
from subprocess import run
|
|
32
33
|
import yaml
|
|
33
34
|
|
|
@@ -54,7 +55,8 @@ class PlatformNotSupported(Exception):
|
|
|
54
55
|
class Platform(Flag):
|
|
55
56
|
MAC = auto()
|
|
56
57
|
UBUNTU = auto()
|
|
57
|
-
|
|
58
|
+
WSL = auto()
|
|
59
|
+
SUPPORTED = MAC | UBUNTU | WSL
|
|
58
60
|
UNSUPPORTED = 0
|
|
59
61
|
|
|
60
62
|
@classmethod
|
|
@@ -63,6 +65,11 @@ class Platform(Flag):
|
|
|
63
65
|
if system_name == "Darwin":
|
|
64
66
|
return cls.MAC
|
|
65
67
|
if system_name == "Linux":
|
|
68
|
+
try:
|
|
69
|
+
if "microsoft" in Path("/proc/version").read_text().lower():
|
|
70
|
+
return cls.WSL
|
|
71
|
+
except OSError:
|
|
72
|
+
pass
|
|
66
73
|
return cls.UBUNTU
|
|
67
74
|
return cls.UNSUPPORTED
|
|
68
75
|
|
|
@@ -70,16 +77,16 @@ class Platform(Flag):
|
|
|
70
77
|
def package_manager(cls, brew_cask=False):
|
|
71
78
|
"""Returns the command for the platform's package manager.
|
|
72
79
|
"""
|
|
73
|
-
|
|
74
|
-
if
|
|
75
|
-
if brew_cask:
|
|
80
|
+
p = cls.detect()
|
|
81
|
+
if p == cls.MAC:
|
|
82
|
+
if brew_cask:
|
|
76
83
|
return "brew install --cask "
|
|
77
84
|
else:
|
|
78
85
|
return "brew install "
|
|
79
|
-
elif
|
|
86
|
+
elif p in (cls.UBUNTU, cls.WSL):
|
|
80
87
|
return "sudo apt install "
|
|
81
88
|
else:
|
|
82
|
-
raise
|
|
89
|
+
raise PlatformNotSupported()
|
|
83
90
|
|
|
84
91
|
def default_work_dir(teacher=False):
|
|
85
92
|
dirname = "making_with_code_teacher" if teacher else "making_with_code"
|
|
@@ -120,7 +127,7 @@ def choose_work_dir(default=None, teacher=False):
|
|
|
120
127
|
default=default or default_work_dir(teacher=teacher),
|
|
121
128
|
type=click.Path(path_type=Path),
|
|
122
129
|
)
|
|
123
|
-
work_dir = work_dir.expanduser()
|
|
130
|
+
work_dir = work_dir.expanduser().resolve()
|
|
124
131
|
if work_dir.is_file():
|
|
125
132
|
click.echo(error("There's already a file at that location."))
|
|
126
133
|
elif work_dir.exists():
|
|
@@ -168,15 +175,15 @@ def choose_editor(default=None):
|
|
|
168
175
|
click.echo(error(f"Couldn't find {ed}. Double-check that it's installed."))
|
|
169
176
|
|
|
170
177
|
def editor_installed(ed):
|
|
171
|
-
return
|
|
178
|
+
return shutil.which(ed) is not None
|
|
172
179
|
|
|
173
180
|
def get_shell_name():
|
|
174
181
|
shellpath = run("echo $SHELL", shell=True, capture_output=True, text=True)
|
|
175
182
|
return shellpath.stdout.split('/')[-1].strip()
|
|
176
183
|
|
|
177
184
|
def get_mwc_rc_path():
|
|
178
|
-
xdg_config_home =
|
|
179
|
-
return xdg_config_home / "mwc" / "shell_config.sh"
|
|
185
|
+
xdg_config_home = os.environ.get("XDG_CONFIG_HOME", str(Path.home() / ".config"))
|
|
186
|
+
return Path(xdg_config_home) / "mwc" / "shell_config.sh"
|
|
180
187
|
|
|
181
188
|
def platform_rc_file(debug=False):
|
|
182
189
|
shell = get_shell_name()
|
|
@@ -234,7 +241,7 @@ class SetupTask:
|
|
|
234
241
|
click.echo(debug_fmt(message))
|
|
235
242
|
|
|
236
243
|
def executable_on_path(self, name):
|
|
237
|
-
return
|
|
244
|
+
return shutil.which(name) is not None
|
|
238
245
|
|
|
239
246
|
class WriteMWCShellConfig(SetupTask):
|
|
240
247
|
description = "Write the MWC shell config file to ~/.config/mwc/shell_config.sh or XDG_CONFIG_HOME"
|
|
@@ -319,7 +326,7 @@ class SuppressDirenvDiffs(SetupTask):
|
|
|
319
326
|
tomlkit.dump(direnv_conf, fh)
|
|
320
327
|
|
|
321
328
|
def get_direnv_config_file(self):
|
|
322
|
-
config_home = os.environ.get("XDG_CONFIG_HOME", Path.home() / ".config")
|
|
329
|
+
config_home = os.environ.get("XDG_CONFIG_HOME", str(Path.home() / ".config"))
|
|
323
330
|
return Path(config_home) / "direnv" / "direnv.toml"
|
|
324
331
|
|
|
325
332
|
|
|
@@ -328,12 +335,13 @@ class InstallXCode(SetupTask):
|
|
|
328
335
|
platform = Platform.MAC
|
|
329
336
|
|
|
330
337
|
def is_complete(self):
|
|
331
|
-
|
|
338
|
+
result = run("xcode-select -p", shell=True, capture_output=True)
|
|
339
|
+
return result.returncode == 0 and bool(result.stdout)
|
|
332
340
|
|
|
333
341
|
def run_task(self):
|
|
334
342
|
msg = (
|
|
335
|
-
"Installing Xcode... (this may take a while)"
|
|
336
|
-
"Please click \"Install\" and accept the license agreement.
|
|
343
|
+
"Installing Xcode... (this may take a while)\n"
|
|
344
|
+
"Please click \"Install\" and accept the license agreement."
|
|
337
345
|
)
|
|
338
346
|
click.echo(address(msg))
|
|
339
347
|
run("xcode-select --install", shell=True, check=True)
|
|
@@ -358,7 +366,7 @@ class InstallPackage(SetupTask):
|
|
|
358
366
|
system_platform = Platform.detect()
|
|
359
367
|
if system_platform == Platform.MAC:
|
|
360
368
|
return self.brew_name
|
|
361
|
-
elif system_platform
|
|
369
|
+
elif system_platform in (Platform.UBUNTU, Platform.WSL):
|
|
362
370
|
return self.apt_name
|
|
363
371
|
else:
|
|
364
372
|
raise PlatformNotSupported()
|
|
@@ -390,17 +398,16 @@ class InstallTree(InstallPackage):
|
|
|
390
398
|
executable_name = brew_name = apt_name = nix_name = "tree"
|
|
391
399
|
|
|
392
400
|
class InstallVSCode(InstallPackage):
|
|
393
|
-
platform = Platform.MAC
|
|
401
|
+
platform = Platform.MAC | Platform.UBUNTU | Platform.WSL
|
|
394
402
|
executable_name = "code"
|
|
395
403
|
brew_name = "visual-studio-code"
|
|
396
|
-
|
|
404
|
+
brew_cask = True
|
|
397
405
|
|
|
398
406
|
def run_task(self):
|
|
399
|
-
|
|
400
|
-
if platform & Platform.UBUNTU:
|
|
407
|
+
if Platform.detect() in (Platform.UBUNTU, Platform.WSL):
|
|
401
408
|
run("sudo snap install --classic code", shell=True, check=True)
|
|
402
409
|
else:
|
|
403
|
-
|
|
410
|
+
super().run_task()
|
|
404
411
|
|
|
405
412
|
class InstallImageMagick(InstallPackage):
|
|
406
413
|
executable_name = "magick"
|
|
@@ -419,50 +426,24 @@ class InstallScipy(InstallPackage):
|
|
|
419
426
|
def is_complete(self):
|
|
420
427
|
return find_spec("scipy") is not None
|
|
421
428
|
|
|
422
|
-
class
|
|
423
|
-
""
|
|
424
|
-
|
|
425
|
-
"""
|
|
426
|
-
description = "Configure git"
|
|
427
|
-
|
|
428
|
-
editorcmds = {
|
|
429
|
-
"atom": '"atom --wait"',
|
|
430
|
-
"code": '"code --wait"',
|
|
431
|
-
"subl": '"subl -n -w"',
|
|
432
|
-
"mate": '"mate -w"',
|
|
433
|
-
"vim": "vim",
|
|
434
|
-
"emacs": "emacs",
|
|
435
|
-
}
|
|
436
|
-
|
|
437
|
-
def is_complete(self):
|
|
438
|
-
if self.settings.get("skip_git_config"):
|
|
439
|
-
confirm("Skipping git configuration because 'skip_git_config' is set in MWC settings")
|
|
440
|
-
return True
|
|
441
|
-
for key, value in self.get_expected_git_config().items():
|
|
442
|
-
expected = value.strip().strip('"')
|
|
443
|
-
observed = self.read_git_config(key).strip().strip('"')
|
|
444
|
-
if expected != observed:
|
|
445
|
-
return False
|
|
446
|
-
return True
|
|
429
|
+
class InstallOllama(InstallPackage):
|
|
430
|
+
executable_name = "ollama"
|
|
431
|
+
brew_name = "ollama"
|
|
447
432
|
|
|
448
433
|
def run_task(self):
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
run(
|
|
434
|
+
if Platform.detect() in (Platform.UBUNTU, Platform.WSL):
|
|
435
|
+
click.echo(address("Installing ollama..."))
|
|
436
|
+
run("curl -fsSL https://ollama.com/install.sh | sh", shell=True, check=True)
|
|
437
|
+
else:
|
|
438
|
+
super().run_task()
|
|
452
439
|
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
440
|
+
class InstallPandoc(InstallPackage):
|
|
441
|
+
executable_name = brew_name = apt_name = "pandoc"
|
|
442
|
+
|
|
443
|
+
class InstallPoppler(InstallPackage):
|
|
444
|
+
executable_name = "pdftotext"
|
|
445
|
+
brew_name = "poppler"
|
|
446
|
+
apt_name = "poppler-utils"
|
|
457
447
|
|
|
458
|
-
def get_expected_git_config(self):
|
|
459
|
-
"""Returns a dict containing expected git configuration settings.
|
|
460
|
-
"""
|
|
461
|
-
git_config = {"init.defaultBranch": "main"}
|
|
462
|
-
if self.settings.get('editor') in self.editorcmds:
|
|
463
|
-
git_config["core.editor"] = self.editorcmds[self.settings.get('editor')]
|
|
464
|
-
git_config["user.name"] = self.settings.get('git_name', self.settings['mwc_username'])
|
|
465
|
-
git_config["user.email"] = self.settings.get('git_email', 'nobody@makingwithcode.org')
|
|
466
|
-
return git_config
|
|
467
448
|
|
|
468
449
|
|
|
@@ -1,24 +1,32 @@
|
|
|
1
1
|
import click
|
|
2
2
|
from csv import DictWriter
|
|
3
3
|
from tabulate import tabulate
|
|
4
|
-
from making_with_code_cli.settings import read_settings
|
|
4
|
+
from making_with_code_cli.settings import read_settings, get_settings_path
|
|
5
5
|
from making_with_code_cli.mwc_accounts_api import MWCAccountsAPI
|
|
6
6
|
from making_with_code_cli.styles import (
|
|
7
7
|
info,
|
|
8
|
+
debug as debug_fmt,
|
|
8
9
|
)
|
|
9
10
|
from making_with_code_cli.teach.setup import check_required_teacher_settings
|
|
10
11
|
from making_with_code_cli.teach.section.create import create_section
|
|
11
|
-
from making_with_code_cli.teach.section.edit import edit_section
|
|
12
12
|
from making_with_code_cli.teach.section.show import show_section
|
|
13
|
+
from making_with_code_cli.teach.section.edit import edit_section
|
|
14
|
+
from making_with_code_cli.teach.section.delete import delete_section
|
|
15
|
+
from making_with_code_cli.decorators import handle_mwc_errors
|
|
13
16
|
|
|
14
17
|
@click.group(invoke_without_command=True)
|
|
15
18
|
@click.pass_context
|
|
16
19
|
@click.option("--config", help="Path to config file (default: ~/.mwc)")
|
|
17
20
|
@click.option("-o", "--outfile", help="Save results as csv")
|
|
18
|
-
|
|
21
|
+
@click.option("--debug", is_flag=True, help="Show debug-level output")
|
|
22
|
+
@handle_mwc_errors
|
|
23
|
+
def section(ctx, config, outfile, debug):
|
|
19
24
|
"Manage sections of students"
|
|
20
25
|
if ctx.invoked_subcommand is None:
|
|
21
26
|
settings = read_settings(config)
|
|
27
|
+
if debug:
|
|
28
|
+
sp = get_settings_path(config)
|
|
29
|
+
click.echo(debug_fmt(f"Reading settings from {sp}"))
|
|
22
30
|
if not check_required_teacher_settings(settings):
|
|
23
31
|
return
|
|
24
32
|
api = MWCAccountsAPI(settings.get('mwc_accounts_url'))
|
|
@@ -42,6 +50,7 @@ def summarize_sections(section_data):
|
|
|
42
50
|
return section_data
|
|
43
51
|
|
|
44
52
|
section.add_command(create_section)
|
|
45
|
-
section.add_command(edit_section)
|
|
46
53
|
section.add_command(show_section)
|
|
54
|
+
section.add_command(edit_section)
|
|
55
|
+
section.add_command(delete_section)
|
|
47
56
|
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import click
|
|
2
|
+
from making_with_code_cli.settings import read_settings
|
|
3
|
+
from making_with_code_cli.mwc_accounts_api import MWCAccountsAPI
|
|
4
|
+
from making_with_code_cli.styles import (
|
|
5
|
+
address,
|
|
6
|
+
error,
|
|
7
|
+
info,
|
|
8
|
+
question,
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
@click.command('delete')
|
|
12
|
+
@click.argument("slug")
|
|
13
|
+
@click.option("--config", help="Path to config file (default: ~/.mwc)")
|
|
14
|
+
def delete_section(slug, config):
|
|
15
|
+
"Delete a section"
|
|
16
|
+
settings = read_settings(config)
|
|
17
|
+
api = MWCAccountsAPI(settings.get('mwc_accounts_url'))
|
|
18
|
+
params = {"slug": slug}
|
|
19
|
+
try:
|
|
20
|
+
response = api.delete_section(settings.get('mwc_accounts_token'), params)
|
|
21
|
+
click.echo(info(f"Deleted section {slug}."))
|
|
22
|
+
except api.RequestFailed as err:
|
|
23
|
+
click.echo(error(f"Error deleting section {slug}."))
|
|
24
|
+
|
|
@@ -4,6 +4,7 @@ from making_with_code_cli.settings import read_settings
|
|
|
4
4
|
from making_with_code_cli.mwc_accounts_api import MWCAccountsAPI
|
|
5
5
|
from making_with_code_cli.curriculum import get_curriculum
|
|
6
6
|
from making_with_code_cli.errors import CurriculumSiteNotAvailable, MWCError
|
|
7
|
+
from making_with_code_cli.decorators import handle_mwc_errors
|
|
7
8
|
from making_with_code_cli.styles import (
|
|
8
9
|
address,
|
|
9
10
|
error,
|
|
@@ -18,10 +19,15 @@ from making_with_code_cli.styles import (
|
|
|
18
19
|
@click.option("--curriculum-site-url", help="URL for curriculum website. e.g. https://makingwithcode.org")
|
|
19
20
|
@click.option("--course-name", help="MWC course name")
|
|
20
21
|
@click.option("--code", help="Code students can use to join the section")
|
|
21
|
-
@click.option("--roster", help="csv file containing student information")
|
|
22
|
-
|
|
22
|
+
@click.option("--roster", click.Path(exists=True), help="csv file containing student information")
|
|
23
|
+
@click.option('-r', "--remove", is_flag=True, help="When using a roster, remove existing students not on roster")
|
|
24
|
+
@click.option('-f', "--force", is_flag=True, help="When using a roster, update existing student passwords")
|
|
25
|
+
@handle_mwc_errors
|
|
26
|
+
def edit_section(slug, config, name, curriculum_site_url, course_name, code, roster, remove, force):
|
|
23
27
|
"Edit an existing section"
|
|
24
28
|
settings = read_settings(config)
|
|
29
|
+
if roster:
|
|
30
|
+
roster_data = read_roster_file(roster)
|
|
25
31
|
api = MWCAccountsAPI(settings.get('mwc_accounts_url'))
|
|
26
32
|
params = {"slug": slug}
|
|
27
33
|
if name:
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import click
|
|
2
2
|
from csv import DictWriter
|
|
3
|
-
from making_with_code_cli.settings import read_settings
|
|
3
|
+
from making_with_code_cli.settings import read_settings, get_settings_path
|
|
4
4
|
from making_with_code_cli.mwc_accounts_api import MWCAccountsAPI
|
|
5
5
|
from making_with_code_cli.styles import (
|
|
6
6
|
address,
|
|
7
|
+
debug as debug_fmt,
|
|
7
8
|
error,
|
|
8
9
|
info,
|
|
9
10
|
question,
|
|
@@ -13,9 +14,13 @@ from making_with_code_cli.styles import (
|
|
|
13
14
|
@click.argument("slug")
|
|
14
15
|
@click.option("--config", help="Path to config file (default: ~/.mwc)")
|
|
15
16
|
@click.option("-o", "--outfile", help="Save results as csv")
|
|
16
|
-
|
|
17
|
+
@click.option("--debug", is_flag=True, help="Show debug-level output")
|
|
18
|
+
def show_section(config, slug, outfile, debug):
|
|
17
19
|
"Show section details"
|
|
18
20
|
settings = read_settings(config)
|
|
21
|
+
if debug:
|
|
22
|
+
sp = get_settings_path(config)
|
|
23
|
+
click.echo(debug_fmt(f"Reading settings from {sp}"))
|
|
19
24
|
api = MWCAccountsAPI(settings.get('mwc_accounts_url'))
|
|
20
25
|
params = {"slug": slug}
|
|
21
26
|
response = api.get_roster(settings.get('mwc_accounts_token'))
|
{making_with_code_cli-5.0.3 → making_with_code_cli-5.2.0}/src/making_with_code_cli/teach/setup.py
RENAMED
|
@@ -46,10 +46,8 @@ def check_required_teacher_settings(settings):
|
|
|
46
46
|
@click.command
|
|
47
47
|
@click.option("--config", help="Path to config file (default: ~/.mwc)")
|
|
48
48
|
@click.option("--debug", is_flag=True, help="Show debug-level output")
|
|
49
|
-
@click.option("--git-name", help="Set git name")
|
|
50
|
-
@click.option("--git-email", help="Set git email address")
|
|
51
49
|
@click.option("--mwc-accounts-url", help="Set URL for MWC accounts server")
|
|
52
|
-
def setup(config, debug,
|
|
50
|
+
def setup(config, debug, mwc_accounts_url):
|
|
53
51
|
"""Configure teacher settings"""
|
|
54
52
|
settings = read_settings(config)
|
|
55
53
|
if debug:
|
|
@@ -57,10 +55,6 @@ def setup(config, debug, git_name, git_email, mwc_accounts_url):
|
|
|
57
55
|
click.echo(debug_fmt(f"Reading settings from {sp}"))
|
|
58
56
|
click.echo(address(INTRO_MESSAGE))
|
|
59
57
|
click.echo()
|
|
60
|
-
if git_name:
|
|
61
|
-
settings['git_name'] = git_name
|
|
62
|
-
if git_email:
|
|
63
|
-
settings['git_email'] = git_email
|
|
64
58
|
if mwc_accounts_url:
|
|
65
59
|
settings['mwc_accounts_url'] = mwc_accounts_url
|
|
66
60
|
settings['mwc_username'] = choose_mwc_username(settings.get("mwc_username"))
|
|
@@ -78,7 +72,7 @@ def setup(config, debug, git_name, git_email, mwc_accounts_url):
|
|
|
78
72
|
token = prompt_mwc_password(settings['mwc_username'], api)
|
|
79
73
|
settings['mwc_accounts_token'] = token
|
|
80
74
|
settings['mwc_git_token'] = status['git_token']
|
|
81
|
-
settings['work_dir'] = str(choose_work_dir(settings.get("work_dir"))
|
|
75
|
+
settings['work_dir'] = str(choose_work_dir(settings.get("work_dir")))
|
|
82
76
|
settings['editor'] = choose_editor(settings.get('editor', 'code'))
|
|
83
77
|
settings['teacher_work_dir'] = str(choose_work_dir(
|
|
84
78
|
settings.get("teacher_work_dir"),
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import click
|
|
2
|
+
from making_with_code_cli.teach.student.create import create_student
|
|
3
|
+
from making_with_code_cli.teach.student.edit import edit_student
|
|
4
|
+
from making_with_code_cli.teach.student.remove import remove_student_from_section
|
|
5
|
+
|
|
6
|
+
@click.group()
|
|
7
|
+
def student():
|
|
8
|
+
"Manage students"
|
|
9
|
+
|
|
10
|
+
student.add_command(create_student)
|
|
11
|
+
student.add_command(edit_student)
|
|
12
|
+
student.add_command(remove_student_from_section)
|
|
@@ -1,19 +1,24 @@
|
|
|
1
1
|
import click
|
|
2
2
|
from tabulate import tabulate
|
|
3
|
-
from making_with_code_cli.settings import read_settings
|
|
3
|
+
from making_with_code_cli.settings import read_settings, get_settings_path
|
|
4
4
|
from making_with_code_cli.mwc_accounts_api import MWCAccountsAPI
|
|
5
5
|
from making_with_code_cli.styles import (
|
|
6
6
|
info,
|
|
7
|
+
debug as debug_fmt,
|
|
7
8
|
error,
|
|
8
9
|
)
|
|
9
10
|
|
|
10
|
-
@click.command("
|
|
11
|
+
@click.command("edit")
|
|
11
12
|
@click.argument("username")
|
|
12
13
|
@click.argument("password")
|
|
13
14
|
@click.option("--config", help="Path to config file (default: ~/.mwc)")
|
|
14
|
-
|
|
15
|
-
|
|
15
|
+
@click.option("--debug", is_flag=True, help="Show debug-level output")
|
|
16
|
+
def edit_student(username, password, config, debug):
|
|
17
|
+
"Edit a student's password"
|
|
16
18
|
settings = read_settings(config)
|
|
19
|
+
if debug:
|
|
20
|
+
sp = get_settings_path(config)
|
|
21
|
+
click.echo(debug_fmt(f"Reading settings from {sp}"))
|
|
17
22
|
api = MWCAccountsAPI(settings.get('mwc_accounts_url'))
|
|
18
23
|
params = {
|
|
19
24
|
"username": username,
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import click
|
|
2
|
+
from making_with_code_cli.settings import read_settings, get_settings_path
|
|
3
|
+
from making_with_code_cli.mwc_accounts_api import MWCAccountsAPI
|
|
4
|
+
from making_with_code_cli.styles import (
|
|
5
|
+
info,
|
|
6
|
+
debug as debug_fmt,
|
|
7
|
+
error,
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
@click.command("remove")
|
|
11
|
+
@click.argument("username")
|
|
12
|
+
@click.argument("section")
|
|
13
|
+
@click.option("--config", help="Path to config file (default: ~/.mwc)")
|
|
14
|
+
@click.option("--debug", is_flag=True, help="Show debug-level output")
|
|
15
|
+
def remove_student_from_section(username, section, config, debug):
|
|
16
|
+
"Remove a student from a section"
|
|
17
|
+
settings = read_settings(config)
|
|
18
|
+
if debug:
|
|
19
|
+
sp = get_settings_path(config)
|
|
20
|
+
click.echo(debug_fmt(f"Reading settings from {sp}"))
|
|
21
|
+
api = MWCAccountsAPI(settings.get('mwc_accounts_url'), debug=debug)
|
|
22
|
+
params = {
|
|
23
|
+
"username": username,
|
|
24
|
+
"section": section,
|
|
25
|
+
}
|
|
26
|
+
try:
|
|
27
|
+
response = api.remove_student_from_section(settings.get('mwc_accounts_token'), params)
|
|
28
|
+
click.echo(info(f"Removed student {username} from {section}."))
|
|
29
|
+
except api.RequestFailed as err:
|
|
30
|
+
click.echo(error(f"Could not remove {params['username']} from {params['section']}:"))
|
|
31
|
+
for field, problems in err.data.items():
|
|
32
|
+
click.echo(error(f" - {field}: {'; '.join(problems)}"))
|
|
33
|
+
|
|
34
|
+
|
|
@@ -22,8 +22,8 @@ from making_with_code_cli.styles import (
|
|
|
22
22
|
def update(config, debug):
|
|
23
23
|
"""Update the MWC work directory"""
|
|
24
24
|
settings = read_settings(config)
|
|
25
|
-
sp = get_settings_path(config)
|
|
26
25
|
if debug:
|
|
26
|
+
sp = get_settings_path(config)
|
|
27
27
|
click.echo(debug_fmt(f"Reading settings from {sp}"))
|
|
28
28
|
if not settings:
|
|
29
29
|
click.echo(error(f"Please run mwc setup first."))
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
from pathlib import Path
|
|
2
|
-
from contextlib import contextmanager
|
|
3
|
-
import dateparser
|
|
4
|
-
import os
|
|
5
|
-
|
|
6
|
-
@contextmanager
|
|
7
|
-
def cd(path):
|
|
8
|
-
"""Sets the cwd within the context
|
|
9
|
-
"""
|
|
10
|
-
origin = Path().resolve()
|
|
11
|
-
try:
|
|
12
|
-
os.chdir(path)
|
|
13
|
-
yield
|
|
14
|
-
finally:
|
|
15
|
-
os.chdir(origin)
|
|
16
|
-
|
|
17
|
-
def in_bounds(value, minimum=None, maximum=None):
|
|
18
|
-
return (not minimum or value >= minimum) and (not maximum or value <= maximum)
|
|
19
|
-
|
|
20
|
-
def date_string(arg):
|
|
21
|
-
return dateparser.parse(arg, settings={'RETURN_AS_TIMEZONE_AWARE': True})
|
|
22
|
-
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import click
|
|
2
|
-
from making_with_code_cli.teach.student.create import create_student
|
|
3
|
-
from making_with_code_cli.teach.student.update import update_student
|
|
4
|
-
|
|
5
|
-
@click.group()
|
|
6
|
-
def student():
|
|
7
|
-
"Manage students"
|
|
8
|
-
|
|
9
|
-
student.add_command(create_student)
|
|
10
|
-
student.add_command(update_student)
|
|
File without changes
|
{making_with_code_cli-5.0.3 → making_with_code_cli-5.2.0}/src/making_with_code_cli/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{making_with_code_cli-5.0.3 → making_with_code_cli-5.2.0}/src/making_with_code_cli/decorators.py
RENAMED
|
File without changes
|
{making_with_code_cli-5.0.3 → making_with_code_cli-5.2.0}/src/making_with_code_cli/errors.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{making_with_code_cli-5.0.3 → making_with_code_cli-5.2.0}/src/making_with_code_cli/git_wrapper.py
RENAMED
|
File without changes
|
{making_with_code_cli-5.0.3 → making_with_code_cli-5.2.0}/src/making_with_code_cli/styles.py
RENAMED
|
File without changes
|
{making_with_code_cli-5.0.3 → making_with_code_cli-5.2.0}/src/making_with_code_cli/submit.py
RENAMED
|
File without changes
|
{making_with_code_cli-5.0.3 → making_with_code_cli-5.2.0}/src/making_with_code_cli/teach/__init__.py
RENAMED
|
File without changes
|
{making_with_code_cli-5.0.3 → making_with_code_cli-5.2.0}/src/making_with_code_cli/teach/assess.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{making_with_code_cli-5.0.3 → making_with_code_cli-5.2.0}/src/making_with_code_cli/teach/log.py
RENAMED
|
File without changes
|
{making_with_code_cli-5.0.3 → making_with_code_cli-5.2.0}/src/making_with_code_cli/teach/patch.py
RENAMED
|
File without changes
|
|
File without changes
|
{making_with_code_cli-5.0.3 → making_with_code_cli-5.2.0}/src/making_with_code_cli/teach/status.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{making_with_code_cli-5.0.3 → making_with_code_cli-5.2.0}/src/making_with_code_cli/teach/update.py
RENAMED
|
File without changes
|
{making_with_code_cli-5.0.3 → making_with_code_cli-5.2.0}/src/making_with_code_cli/version.py
RENAMED
|
File without changes
|