ed-api-client 0.1.2__tar.gz → 0.1.4__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.
@@ -0,0 +1,132 @@
1
+ Metadata-Version: 2.4
2
+ Name: ed-api-client
3
+ Version: 0.1.4
4
+ Summary: A client for interacting with the Ed LMS
5
+ License: MIT
6
+ Author: David Milne
7
+ Author-email: d.n.milne@gmail.com
8
+ Requires-Python: >=3.10,<4.0
9
+ Classifier: License :: OSI Approved :: MIT License
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Programming Language :: Python :: 3.10
12
+ Classifier: Programming Language :: Python :: 3.11
13
+ Classifier: Programming Language :: Python :: 3.12
14
+ Classifier: Programming Language :: Python :: 3.13
15
+ Classifier: Programming Language :: Python :: 3.14
16
+ Requires-Dist: DateTime (>=5.1,<6.0)
17
+ Requires-Dist: pylcs (>=0.1.1,<0.2.0)
18
+ Requires-Dist: python-dateutil (>=2.9.0,<3.0.0)
19
+ Requires-Dist: requests (>=2.31.0,<3.0.0)
20
+ Requires-Dist: websocket-client (>=1.6,<2.0)
21
+ Description-Content-Type: text/markdown
22
+
23
+ # ed-api-client
24
+
25
+ A Python client for the [Ed LMS](https://edstem.org) API, with utilities for analysing student workspace activity.
26
+
27
+ ## Installation
28
+
29
+ ```bash
30
+ pip install ed-api-client
31
+ ```
32
+
33
+ ## Quick start
34
+
35
+ ```python
36
+ from ed_api_client import EdClient
37
+
38
+ client = EdClient(token="your-api-token")
39
+
40
+ # Retrieve all students in a course
41
+ users = client.get_users(course_id=12345, role="student")
42
+
43
+ # Retrieve the module/lesson structure
44
+ modules = client.get_module_structure(course_id=12345)
45
+ for module in modules:
46
+ for lesson in module.lessons:
47
+ print(lesson.title)
48
+
49
+ # Fetch and analyse a student's workspace activity
50
+ log = client.get_workspace_log(workspace_id="abc123")
51
+ files = client.get_scaffold_files(challenge_id=99)
52
+ summary = WorkspaceSummary.from_log(files, log)
53
+
54
+ print(summary.total_active_time)
55
+ for file_id, file in summary.file_summaries.items():
56
+ print(file.path, file.pasted_proportion, file.cursor_entropy)
57
+ ```
58
+
59
+ Full API reference: https://dmilne.gitlab.io/ed-api-client
60
+
61
+ ## Development
62
+
63
+ ### Prerequisites
64
+
65
+ - Python 3.10+
66
+ - [Poetry](https://python-poetry.org/docs/#installation)
67
+
68
+ Install Poetry via Homebrew:
69
+
70
+ ```bash
71
+ brew install poetry
72
+ ```
73
+
74
+ ### Setup
75
+
76
+ Clone the repository and install all dependencies (including dev dependencies):
77
+
78
+ ```bash
79
+ git clone https://gitlab.com/dmilne/ed-api-client.git
80
+ cd ed-api-client
81
+ poetry install --with dev
82
+ ```
83
+
84
+ ### Running tests
85
+
86
+ ```bash
87
+ poetry run pytest tests/ -v
88
+ ```
89
+
90
+ Tests use the `responses` library to mock HTTP calls — no real Ed credentials are needed.
91
+
92
+ ### Viewing the docs locally
93
+
94
+ ```bash
95
+ poetry run mkdocs serve
96
+ ```
97
+
98
+ Then open http://localhost:8000.
99
+
100
+ ## Publishing a new version
101
+
102
+ 1. Bump the version:
103
+
104
+ ```bash
105
+ poetry version patch # or: minor, major
106
+ ```
107
+
108
+ 2. Configure your PyPI token (first time only):
109
+
110
+ ```bash
111
+ poetry config pypi-token.pypi your-token-here
112
+ ```
113
+
114
+ Get a token at https://pypi.org/manage/account/token/.
115
+
116
+ 3. Build and publish:
117
+
118
+ ```bash
119
+ poetry publish --build
120
+ ```
121
+
122
+ ## CI/CD
123
+
124
+ The GitLab pipeline (`.gitlab-ci.yml`) runs on every push:
125
+
126
+ - **test** — runs `pytest` against Python 3.11
127
+ - **pages** (main branch only) — builds and deploys the MkDocs site to GitLab Pages at https://dmilne.gitlab.io/ed-api-client
128
+
129
+ ## License
130
+
131
+ MIT
132
+
@@ -0,0 +1,109 @@
1
+ # ed-api-client
2
+
3
+ A Python client for the [Ed LMS](https://edstem.org) API, with utilities for analysing student workspace activity.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ pip install ed-api-client
9
+ ```
10
+
11
+ ## Quick start
12
+
13
+ ```python
14
+ from ed_api_client import EdClient
15
+
16
+ client = EdClient(token="your-api-token")
17
+
18
+ # Retrieve all students in a course
19
+ users = client.get_users(course_id=12345, role="student")
20
+
21
+ # Retrieve the module/lesson structure
22
+ modules = client.get_module_structure(course_id=12345)
23
+ for module in modules:
24
+ for lesson in module.lessons:
25
+ print(lesson.title)
26
+
27
+ # Fetch and analyse a student's workspace activity
28
+ log = client.get_workspace_log(workspace_id="abc123")
29
+ files = client.get_scaffold_files(challenge_id=99)
30
+ summary = WorkspaceSummary.from_log(files, log)
31
+
32
+ print(summary.total_active_time)
33
+ for file_id, file in summary.file_summaries.items():
34
+ print(file.path, file.pasted_proportion, file.cursor_entropy)
35
+ ```
36
+
37
+ Full API reference: https://dmilne.gitlab.io/ed-api-client
38
+
39
+ ## Development
40
+
41
+ ### Prerequisites
42
+
43
+ - Python 3.10+
44
+ - [Poetry](https://python-poetry.org/docs/#installation)
45
+
46
+ Install Poetry via Homebrew:
47
+
48
+ ```bash
49
+ brew install poetry
50
+ ```
51
+
52
+ ### Setup
53
+
54
+ Clone the repository and install all dependencies (including dev dependencies):
55
+
56
+ ```bash
57
+ git clone https://gitlab.com/dmilne/ed-api-client.git
58
+ cd ed-api-client
59
+ poetry install --with dev
60
+ ```
61
+
62
+ ### Running tests
63
+
64
+ ```bash
65
+ poetry run pytest tests/ -v
66
+ ```
67
+
68
+ Tests use the `responses` library to mock HTTP calls — no real Ed credentials are needed.
69
+
70
+ ### Viewing the docs locally
71
+
72
+ ```bash
73
+ poetry run mkdocs serve
74
+ ```
75
+
76
+ Then open http://localhost:8000.
77
+
78
+ ## Publishing a new version
79
+
80
+ 1. Bump the version:
81
+
82
+ ```bash
83
+ poetry version patch # or: minor, major
84
+ ```
85
+
86
+ 2. Configure your PyPI token (first time only):
87
+
88
+ ```bash
89
+ poetry config pypi-token.pypi your-token-here
90
+ ```
91
+
92
+ Get a token at https://pypi.org/manage/account/token/.
93
+
94
+ 3. Build and publish:
95
+
96
+ ```bash
97
+ poetry publish --build
98
+ ```
99
+
100
+ ## CI/CD
101
+
102
+ The GitLab pipeline (`.gitlab-ci.yml`) runs on every push:
103
+
104
+ - **test** — runs `pytest` against Python 3.11
105
+ - **pages** (main branch only) — builds and deploys the MkDocs site to GitLab Pages at https://dmilne.gitlab.io/ed-api-client
106
+
107
+ ## License
108
+
109
+ MIT
@@ -3,10 +3,60 @@ from typing import List, Dict, Set
3
3
  import datetime
4
4
  import dateutil.parser
5
5
  import pylcs
6
+ import math
6
7
  import re
7
8
 
8
9
  from ed_api_client.websockets import File
9
10
 
11
+ class StringSet:
12
+ def __init__(self):
13
+ self.strings = set()
14
+
15
+ def add(self, s: str):
16
+
17
+ if self.is_subsumed(s):
18
+ return
19
+
20
+ for candidate in list(self.strings):
21
+ if candidate in s.strip():
22
+ self.strings.remove(candidate)
23
+
24
+ self.strings.add(s.strip())
25
+
26
+ def contains(self, s: str) -> bool:
27
+ return s.strip() in self.strings
28
+
29
+ def is_subsumed(self, s: str) -> bool:
30
+ stripped_s = s.strip()
31
+ for candidate in self.strings:
32
+ if candidate in stripped_s:
33
+ return True
34
+ return False
35
+
36
+
37
+
38
+
39
+ def _shannon_entropy(values: List[float], n_bins: int = 20) -> float:
40
+ """Normalised Shannon entropy in [0, 1] over a histogram of *values*.
41
+
42
+ Returns 0 if there are fewer than 2 values or all values are identical
43
+ (no information), and 1 if values are spread perfectly uniformly across
44
+ all bins (maximum uncertainty).
45
+ """
46
+ if len(values) < 2:
47
+ return 0.0
48
+ v_min, v_max = min(values), max(values)
49
+ if v_min == v_max:
50
+ return 0.0
51
+ span = v_max - v_min
52
+ bins = [0] * n_bins
53
+ for v in values:
54
+ idx = min(int((v - v_min) / span * n_bins), n_bins - 1)
55
+ bins[idx] += 1
56
+ total = len(values)
57
+ entropy = -sum((c / total) * math.log2(c / total) for c in bins if c > 0)
58
+ return entropy / math.log2(n_bins)
59
+
10
60
 
11
61
  def get_overlap(idx):
12
62
 
@@ -399,6 +449,7 @@ class FileSummary(File):
399
449
  typing_time: datetime.timedelta = datetime.timedelta(0)
400
450
  prev_typing_time: datetime.datetime = None
401
451
 
452
+ selections: List[str] = field(default_factory=list)
402
453
  external_pastes: List[str] = field(default_factory=list)
403
454
 
404
455
  pasted_proportion: float = 0
@@ -411,6 +462,18 @@ class FileSummary(File):
411
462
 
412
463
  edit_count: int = 0
413
464
 
465
+ cursor_entropy: float = 0.0
466
+ typing_time_entropy: float = 0.0
467
+
468
+ multiline_selection_count: int = 0
469
+ multiline_deletion_count: int = 0
470
+
471
+ # private accumulators — populated during from_log, not meaningful standalone
472
+ _insert_positions: List[float] = field(default_factory=list)
473
+ _insert_intervals: List[float] = field(default_factory=list)
474
+ _prev_insert_time: datetime.datetime = None
475
+ _pending_deletion: str = ""
476
+
414
477
  def from_file(file: File):
415
478
  fs = FileSummary(file.id, file.path, file.content)
416
479
  fs.pasted_mask = get_masked_section(file.content, mask_char='#')
@@ -502,13 +565,13 @@ class WorkspaceSummary:
502
565
  for file in files:
503
566
  file_summaries[file.id] = FileSummary.from_file(file)
504
567
 
505
- selections = []
506
- selected_content = set()
568
+ selected_content = StringSet()
507
569
 
508
570
  for selection in acceptable_selections:
509
- selected_content.add(selection.strip())
571
+ selected_content.add(selection)
510
572
 
511
573
  curr_selection = None
574
+ pending_selection = None
512
575
  for event in log.events:
513
576
 
514
577
  if event is None:
@@ -518,6 +581,18 @@ class WorkspaceSummary:
518
581
  if event.passed and stop_at_first_successful_submission:
519
582
  break
520
583
 
584
+ # Flush the buffered selection when the cursor-event run ends.
585
+ if event.type != "cursor" and pending_selection is not None:
586
+ sel_text, sel_file_id = pending_selection[2], pending_selection[3]
587
+ selected_content.add(sel_text.strip())
588
+ sel_file = file_summaries.get(sel_file_id)
589
+ if sel_file is not None:
590
+ sel_file.selections.append(sel_text.strip())
591
+ if '\n' in sel_text:
592
+ sel_file.multiline_selection_count += 1
593
+
594
+ pending_selection = None
595
+
521
596
  if event.type != "cursor" and event.type != "edit" and event.type != "init":
522
597
  continue
523
598
 
@@ -540,13 +615,13 @@ class WorkspaceSummary:
540
615
  if event.type == "cursor":
541
616
  if event.cursor_start is None or event.cursor_end is None:
542
617
  curr_selection = None
618
+ pending_selection = None
543
619
  continue
544
620
 
545
621
  if event.cursor_end - event.cursor_start > min_suspicious_paste_chars:
546
622
  curr_selection = (event.cursor_start, event.cursor_end)
547
623
  selection = file.content[event.cursor_start : event.cursor_end]
548
- selected_content.add(selection.strip())
549
- selections.append(Selection(event.cursor_start, event.cursor_end, selection, event.file_id, len(file.content)))
624
+ pending_selection = (event.cursor_start, event.cursor_end, selection, event.file_id, len(file.content))
550
625
 
551
626
  if event.type != "edit":
552
627
  continue
@@ -564,12 +639,15 @@ class WorkspaceSummary:
564
639
 
565
640
  file.prev_typing_time = event.at
566
641
 
642
+ has_insert = False
567
643
  for edit in event.edits:
568
644
  if edit.type == "skip":
569
645
  pos = pos + edit.value
570
646
  curr_selection = None
571
647
 
572
648
  if edit.type == "delete":
649
+ deleted_text = file.content[pos : pos + edit.value]
650
+ file._pending_deletion += deleted_text
573
651
  file.deleted_masked_content.append(
574
652
  merge_masked_contents(
575
653
  file.pasted_mask[pos : pos + edit.value],
@@ -613,13 +691,35 @@ class WorkspaceSummary:
613
691
  )
614
692
 
615
693
  if len(edit.value) > min_suspicious_paste_chars:
616
- if edit.value.strip() not in selected_content:
694
+ if not selected_content.is_subsumed(edit.value):
617
695
  file.external_pastes.append(edit.value)
618
696
 
697
+ file._insert_positions.append(pos / max(len(file.content), 1))
698
+ has_insert = True
619
699
  file.content = file.content[:pos] + edit.value + file.content[pos:]
620
700
  file.pasted_mask = file.pasted_mask[:pos] + edit.value + file.pasted_mask[pos:]
621
701
  pos = pos + len(edit.value)
622
702
 
703
+ if has_insert:
704
+ if file._prev_insert_time is not None:
705
+ file._insert_intervals.append(
706
+ (event.at - file._prev_insert_time).total_seconds()
707
+ )
708
+ file._prev_insert_time = event.at
709
+ if file._pending_deletion:
710
+ if '\n' in file._pending_deletion:
711
+ file.multiline_deletion_count += 1
712
+ file._pending_deletion = ""
713
+
714
+ if pending_selection is not None:
715
+ sel_text, sel_file_id = pending_selection[2], pending_selection[3]
716
+ selected_content.add(sel_text.strip())
717
+ selections.append(Selection(*pending_selection))
718
+ if '\n' in sel_text:
719
+ sel_file = file_summaries.get(sel_file_id)
720
+ if sel_file is not None:
721
+ sel_file.multiline_selection_count += 1
722
+
623
723
  total_active_time = datetime.timedelta(0)
624
724
  total_typing_time = datetime.timedelta(0)
625
725
  for file in file_summaries.values():
@@ -644,4 +744,10 @@ class WorkspaceSummary:
644
744
  file.pasted_proportion = get_masked_proportion(file.pasted_mask)
645
745
  file.transcribed_proportion = get_masked_proportion(file.transcribed_mask)
646
746
 
747
+ file.cursor_entropy = _shannon_entropy(file._insert_positions)
748
+ file.typing_time_entropy = _shannon_entropy(file._insert_intervals)
749
+
750
+ if file._pending_deletion and '\n' in file._pending_deletion:
751
+ file.multiline_deletion_count += 1
752
+
647
753
  return WorkspaceSummary(total_active_time, total_typing_time, file_summaries, selections)
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "ed-api-client"
3
- version = "0.1.2"
3
+ version = "0.1.4"
4
4
  description = "A client for interacting with the Ed LMS"
5
5
  authors = ["David Milne <d.n.milne@gmail.com>"]
6
6
  license = "MIT"
@@ -1,115 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: ed-api-client
3
- Version: 0.1.2
4
- Summary: A client for interacting with the Ed LMS
5
- License: MIT
6
- Author: David Milne
7
- Author-email: d.n.milne@gmail.com
8
- Requires-Python: >=3.10,<4.0
9
- Classifier: License :: OSI Approved :: MIT License
10
- Classifier: Programming Language :: Python :: 3
11
- Classifier: Programming Language :: Python :: 3.10
12
- Classifier: Programming Language :: Python :: 3.11
13
- Classifier: Programming Language :: Python :: 3.12
14
- Classifier: Programming Language :: Python :: 3.13
15
- Classifier: Programming Language :: Python :: 3.14
16
- Requires-Dist: DateTime (>=5.1,<6.0)
17
- Requires-Dist: pylcs (>=0.1.1,<0.2.0)
18
- Requires-Dist: python-dateutil (>=2.9.0,<3.0.0)
19
- Requires-Dist: requests (>=2.31.0,<3.0.0)
20
- Requires-Dist: websocket-client (>=1.6,<2.0)
21
- Description-Content-Type: text/markdown
22
-
23
- # ed-api-client
24
-
25
-
26
-
27
- ## Getting started
28
-
29
- To make it easy for you to get started with GitLab, here's a list of recommended next steps.
30
-
31
- Already a pro? Just edit this README.md and make it your own. Want to make it easy? [Use the template at the bottom](#editing-this-readme)!
32
-
33
- ## Add your files
34
-
35
- - [ ] [Create](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#create-a-file) or [upload](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#upload-a-file) files
36
- - [ ] [Add files using the command line](https://docs.gitlab.com/ee/gitlab-basics/add-file.html#add-a-file-using-the-command-line) or push an existing Git repository with the following command:
37
-
38
- ```
39
- cd existing_repo
40
- git remote add origin https://gitlab.com/dmilne/ed-api-client.git
41
- git branch -M main
42
- git push -uf origin main
43
- ```
44
-
45
- ## Integrate with your tools
46
-
47
- - [ ] [Set up project integrations](https://gitlab.com/dmilne/ed-api-client/-/settings/integrations)
48
-
49
- ## Collaborate with your team
50
-
51
- - [ ] [Invite team members and collaborators](https://docs.gitlab.com/ee/user/project/members/)
52
- - [ ] [Create a new merge request](https://docs.gitlab.com/ee/user/project/merge_requests/creating_merge_requests.html)
53
- - [ ] [Automatically close issues from merge requests](https://docs.gitlab.com/ee/user/project/issues/managing_issues.html#closing-issues-automatically)
54
- - [ ] [Enable merge request approvals](https://docs.gitlab.com/ee/user/project/merge_requests/approvals/)
55
- - [ ] [Automatically merge when pipeline succeeds](https://docs.gitlab.com/ee/user/project/merge_requests/merge_when_pipeline_succeeds.html)
56
-
57
- ## Test and Deploy
58
-
59
- Use the built-in continuous integration in GitLab.
60
-
61
- - [ ] [Get started with GitLab CI/CD](https://docs.gitlab.com/ee/ci/quick_start/index.html)
62
- - [ ] [Analyze your code for known vulnerabilities with Static Application Security Testing(SAST)](https://docs.gitlab.com/ee/user/application_security/sast/)
63
- - [ ] [Deploy to Kubernetes, Amazon EC2, or Amazon ECS using Auto Deploy](https://docs.gitlab.com/ee/topics/autodevops/requirements.html)
64
- - [ ] [Use pull-based deployments for improved Kubernetes management](https://docs.gitlab.com/ee/user/clusters/agent/)
65
- - [ ] [Set up protected environments](https://docs.gitlab.com/ee/ci/environments/protected_environments.html)
66
-
67
- ***
68
-
69
- # Editing this README
70
-
71
- When you're ready to make this README your own, just edit this file and use the handy template below (or feel free to structure it however you want - this is just a starting point!). Thank you to [makeareadme.com](https://www.makeareadme.com/) for this template.
72
-
73
- ## Suggestions for a good README
74
- Every project is different, so consider which of these sections apply to yours. The sections used in the template are suggestions for most open source projects. Also keep in mind that while a README can be too long and detailed, too long is better than too short. If you think your README is too long, consider utilizing another form of documentation rather than cutting out information.
75
-
76
- ## Name
77
- Choose a self-explaining name for your project.
78
-
79
- ## Description
80
- Let people know what your project can do specifically. Provide context and add a link to any reference visitors might be unfamiliar with. A list of Features or a Background subsection can also be added here. If there are alternatives to your project, this is a good place to list differentiating factors.
81
-
82
- ## Badges
83
- On some READMEs, you may see small images that convey metadata, such as whether or not all the tests are passing for the project. You can use Shields to add some to your README. Many services also have instructions for adding a badge.
84
-
85
- ## Visuals
86
- Depending on what you are making, it can be a good idea to include screenshots or even a video (you'll frequently see GIFs rather than actual videos). Tools like ttygif can help, but check out Asciinema for a more sophisticated method.
87
-
88
- ## Installation
89
- Within a particular ecosystem, there may be a common way of installing things, such as using Yarn, NuGet, or Homebrew. However, consider the possibility that whoever is reading your README is a novice and would like more guidance. Listing specific steps helps remove ambiguity and gets people to using your project as quickly as possible. If it only runs in a specific context like a particular programming language version or operating system or has dependencies that have to be installed manually, also add a Requirements subsection.
90
-
91
- ## Usage
92
- Use examples liberally, and show the expected output if you can. It's helpful to have inline the smallest example of usage that you can demonstrate, while providing links to more sophisticated examples if they are too long to reasonably include in the README.
93
-
94
- ## Support
95
- Tell people where they can go to for help. It can be any combination of an issue tracker, a chat room, an email address, etc.
96
-
97
- ## Roadmap
98
- If you have ideas for releases in the future, it is a good idea to list them in the README.
99
-
100
- ## Contributing
101
- State if you are open to contributions and what your requirements are for accepting them.
102
-
103
- For people who want to make changes to your project, it's helpful to have some documentation on how to get started. Perhaps there is a script that they should run or some environment variables that they need to set. Make these steps explicit. These instructions could also be useful to your future self.
104
-
105
- You can also document commands to lint the code or run tests. These steps help to ensure high code quality and reduce the likelihood that the changes inadvertently break something. Having instructions for running tests is especially helpful if it requires external setup, such as starting a Selenium server for testing in a browser.
106
-
107
- ## Authors and acknowledgment
108
- Show your appreciation to those who have contributed to the project.
109
-
110
- ## License
111
- For open source projects, say how it is licensed.
112
-
113
- ## Project status
114
- If you have run out of energy or time for your project, put a note at the top of the README saying that development has slowed down or stopped completely. Someone may choose to fork your project or volunteer to step in as a maintainer or owner, allowing your project to keep going. You can also make an explicit request for maintainers.
115
-
@@ -1,92 +0,0 @@
1
- # ed-api-client
2
-
3
-
4
-
5
- ## Getting started
6
-
7
- To make it easy for you to get started with GitLab, here's a list of recommended next steps.
8
-
9
- Already a pro? Just edit this README.md and make it your own. Want to make it easy? [Use the template at the bottom](#editing-this-readme)!
10
-
11
- ## Add your files
12
-
13
- - [ ] [Create](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#create-a-file) or [upload](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#upload-a-file) files
14
- - [ ] [Add files using the command line](https://docs.gitlab.com/ee/gitlab-basics/add-file.html#add-a-file-using-the-command-line) or push an existing Git repository with the following command:
15
-
16
- ```
17
- cd existing_repo
18
- git remote add origin https://gitlab.com/dmilne/ed-api-client.git
19
- git branch -M main
20
- git push -uf origin main
21
- ```
22
-
23
- ## Integrate with your tools
24
-
25
- - [ ] [Set up project integrations](https://gitlab.com/dmilne/ed-api-client/-/settings/integrations)
26
-
27
- ## Collaborate with your team
28
-
29
- - [ ] [Invite team members and collaborators](https://docs.gitlab.com/ee/user/project/members/)
30
- - [ ] [Create a new merge request](https://docs.gitlab.com/ee/user/project/merge_requests/creating_merge_requests.html)
31
- - [ ] [Automatically close issues from merge requests](https://docs.gitlab.com/ee/user/project/issues/managing_issues.html#closing-issues-automatically)
32
- - [ ] [Enable merge request approvals](https://docs.gitlab.com/ee/user/project/merge_requests/approvals/)
33
- - [ ] [Automatically merge when pipeline succeeds](https://docs.gitlab.com/ee/user/project/merge_requests/merge_when_pipeline_succeeds.html)
34
-
35
- ## Test and Deploy
36
-
37
- Use the built-in continuous integration in GitLab.
38
-
39
- - [ ] [Get started with GitLab CI/CD](https://docs.gitlab.com/ee/ci/quick_start/index.html)
40
- - [ ] [Analyze your code for known vulnerabilities with Static Application Security Testing(SAST)](https://docs.gitlab.com/ee/user/application_security/sast/)
41
- - [ ] [Deploy to Kubernetes, Amazon EC2, or Amazon ECS using Auto Deploy](https://docs.gitlab.com/ee/topics/autodevops/requirements.html)
42
- - [ ] [Use pull-based deployments for improved Kubernetes management](https://docs.gitlab.com/ee/user/clusters/agent/)
43
- - [ ] [Set up protected environments](https://docs.gitlab.com/ee/ci/environments/protected_environments.html)
44
-
45
- ***
46
-
47
- # Editing this README
48
-
49
- When you're ready to make this README your own, just edit this file and use the handy template below (or feel free to structure it however you want - this is just a starting point!). Thank you to [makeareadme.com](https://www.makeareadme.com/) for this template.
50
-
51
- ## Suggestions for a good README
52
- Every project is different, so consider which of these sections apply to yours. The sections used in the template are suggestions for most open source projects. Also keep in mind that while a README can be too long and detailed, too long is better than too short. If you think your README is too long, consider utilizing another form of documentation rather than cutting out information.
53
-
54
- ## Name
55
- Choose a self-explaining name for your project.
56
-
57
- ## Description
58
- Let people know what your project can do specifically. Provide context and add a link to any reference visitors might be unfamiliar with. A list of Features or a Background subsection can also be added here. If there are alternatives to your project, this is a good place to list differentiating factors.
59
-
60
- ## Badges
61
- On some READMEs, you may see small images that convey metadata, such as whether or not all the tests are passing for the project. You can use Shields to add some to your README. Many services also have instructions for adding a badge.
62
-
63
- ## Visuals
64
- Depending on what you are making, it can be a good idea to include screenshots or even a video (you'll frequently see GIFs rather than actual videos). Tools like ttygif can help, but check out Asciinema for a more sophisticated method.
65
-
66
- ## Installation
67
- Within a particular ecosystem, there may be a common way of installing things, such as using Yarn, NuGet, or Homebrew. However, consider the possibility that whoever is reading your README is a novice and would like more guidance. Listing specific steps helps remove ambiguity and gets people to using your project as quickly as possible. If it only runs in a specific context like a particular programming language version or operating system or has dependencies that have to be installed manually, also add a Requirements subsection.
68
-
69
- ## Usage
70
- Use examples liberally, and show the expected output if you can. It's helpful to have inline the smallest example of usage that you can demonstrate, while providing links to more sophisticated examples if they are too long to reasonably include in the README.
71
-
72
- ## Support
73
- Tell people where they can go to for help. It can be any combination of an issue tracker, a chat room, an email address, etc.
74
-
75
- ## Roadmap
76
- If you have ideas for releases in the future, it is a good idea to list them in the README.
77
-
78
- ## Contributing
79
- State if you are open to contributions and what your requirements are for accepting them.
80
-
81
- For people who want to make changes to your project, it's helpful to have some documentation on how to get started. Perhaps there is a script that they should run or some environment variables that they need to set. Make these steps explicit. These instructions could also be useful to your future self.
82
-
83
- You can also document commands to lint the code or run tests. These steps help to ensure high code quality and reduce the likelihood that the changes inadvertently break something. Having instructions for running tests is especially helpful if it requires external setup, such as starting a Selenium server for testing in a browser.
84
-
85
- ## Authors and acknowledgment
86
- Show your appreciation to those who have contributed to the project.
87
-
88
- ## License
89
- For open source projects, say how it is licensed.
90
-
91
- ## Project status
92
- If you have run out of energy or time for your project, put a note at the top of the README saying that development has slowed down or stopped completely. Someone may choose to fork your project or volunteer to step in as a maintainer or owner, allowing your project to keep going. You can also make an explicit request for maintainers.