diff --git a/.github/requirements.txt b/.github/requirements.txt new file mode 100644 index 0000000..f229360 --- /dev/null +++ b/.github/requirements.txt @@ -0,0 +1 @@ +requests diff --git a/.github/standup.py b/.github/standup.py new file mode 100644 index 0000000..cc9fd9a --- /dev/null +++ b/.github/standup.py @@ -0,0 +1,296 @@ +#!/usr/bin/env python3 +""" +Perform standup +""" +from datetime import datetime +from datetime import date +from datetime import timedelta +import os + +import requests + + +GITHUB_OWNER = 'stvstnfrd' +GITHUB_REPO = 'openedx-meta' + + +def post(url, data): + return request_http(url, method='POST', data=data) + + +def get(url): + return request_http(url) + + +def request_http(url, method=None, data=None): + method = method or 'GET' + request_method = getattr(requests, method.lower()) + url = f"https://api.github.com/{url}" + response = None + try: + data = { + # 'key': 'value', + } + params = { + 'q': 'requests+language:python', + } + headers = { + 'Accept': 'application/vnd.github.inertia-preview+json', + } + token = os.environ.get('GITHUB_TOKEN') + if token: + headers.update({ + 'Authorization': f"token {token}", + }) + response = request_method( + url, + data=data, + headers=headers, + # params=params, + ) + except requests.HTTPError as error: + print(f'HTTP error occurred: {error}') + raise + except Exception as error: + print(f'Other error occurred: {error}') + raise + else: + pass + # print('Success!') + if not response: + code = response.status_code + message = response.json()['message'] + raise Exception(f"{code} {message}") + data = response.json() + return data + + +def main(): + """ + Handle the main script logic + """ + board = Board() + print(board) + # board.standup() + + +class Board: + def __init__(self): + self.owner = GITHUB_OWNER + self.repo = GITHUB_REPO + self.days = 5 + self.days_ahead = 3 + self.columns = { + 'done': { + 'id': 14068734, + 'cards': [], + 'authors': {}, + }, + 'doing': { + 'id': 14068727, + 'cards': [], + 'authors': {}, + }, + 'todo': { + 'id': 14068716, + 'cards': [], + 'authors': {}, + }, + } + for key, column in self.columns.items(): + for card in self.get_cards_done(column['id']): + self.columns[key]['cards'].append(card) + if not card.author in self.columns[key]['authors']: + self.columns[key]['authors'][card.author] = [] + self.columns[key]['authors'][card.author].append(card) + + @property + def is_standup_today(self): + standup_time = self.standup_time + if standup_time.weekday() in (5,6): + return False + return True + + @property + def standup_time(self): + now = datetime.now() + start = datetime( + year=now.year, + month=now.month, + day=now.day, + hour=12, + ) + timedelta(days=self.days_ahead) + return start + + @property + def standup_time_window_open(self): + period = timedelta(days=self.days) + stop = self.standup_time - period + return stop + + def get_cards_done(self, column_id): + cards_done = get(f"projects/columns/{column_id}/cards") + for card in cards_done: + entry = Card.from_card(card) + if not entry: + continue + if entry.is_in_timespan(self.standup_time_window_open, self.standup_time): + yield entry + + def standup(self): + title = self.title + body = str(self) + labels = [ + 'standup', + ] + data = { + # 'assignees': self.participants, + 'body': self.body, + # 'labels': labels, + 'title': self.title, + } + print('DATA', data) + response = post(f"repos/{self.owner}/{self.repo}/issues", data=data) + print('RESPONSE', response) + return response + + @property + def participants(self): + assignees = set() + for column_id, column in self.columns.items(): + for author in column['authors'].keys(): + assignees.add(author) + return assignees + + @property + def body(self): + text = str(self) + return text + + @property + def title(self): + timestamp = self.standup_time.date() + text = f"standup: {timestamp}" + return text + + @property + def title_as_markdown(self): + text = f"[{self.title}](https://github.com/{self.owner}/{self.repo}-meta/issues?q=is%3Aissue+sort%3Aupdated-desc)" + return text + + def __str__(self): + if not self.is_standup_today: + return '' + assignees = ', '.join(self.participants) + lines = [ + '---', + f"title: '{self.title}'", + f"assignees: {assignees}", + f"labels: standup", + '---', + ] + lines.append(f"# {self.title_as_markdown}") + for column in ('done', 'doing', 'todo'): + if column == 'done': + marker = 'x' + else: + marker = ' ' + cards = self.columns[column]['authors'] + column_id = self.columns[column]['id'] + lines.append(f"\n## [{column}](https://github.com/{self.owner}/{self.repo}/projects/3#column-{column_id})") + for author, entries in cards.items(): + lines.append(f"\n### [@{author}](https://github.com/{self.owner}/{self.repo}/issues/assigned/{author})\n") + for entry in entries: + lines.append(f"- [{marker}] {entry.text_as_link}") + string = '\n'.join(lines) + return string + + +class Card: + """ + This is a (standup) entry + """ + def __init__(self, author, text, labels=None, url=None, updated_at=None): + self.author = author + self.text = text + self.labels = labels or [] + self.url = url or '' + self.updated_at = updated_at or None + + @property + def text_as_link(self): + text = self.text + url = self.url + text = f"[{text}]({url})" + return text + + @property + def author_as_link(self): + text = self.author + url = f"https://github.com/{GITHUB_OWNER}/{GITHUB_REPO}/issues/assigned/{text}" + text = f"[{text}]({url})" + return text + + def __str__(self): + string = f"{self.author}: {self.text}" + return string + + def __repr__(self): + string = f"{self.author}: {self.text}" + return string + + def is_in_timespan(self, time_older, time_newer): + if self.updated_at < time_older: + return False + if self.updated_at > time_newer: + return False + return True + + @property + def is_discussable(self): + if self.text.startswith('discuss:'): + return True + if 'dicuss' in self.labels: + return True + return False + + @classmethod + def from_card(cls, card): + if card['archived']: + return None + text = card['note'] + author = card['creator']['login'] + updated_at = card['updated_at'][:-1] + updated_at = datetime.fromisoformat(updated_at) + if 'content_url' in card: + url = card['content_url'] + issue_number = int(url.split('/')[-1]) + issue = get(f"repos/{GITHUB_OWNER}/{GITHUB_REPO}/issues/{issue_number}") + entry = cls.from_issue(url, issue) + return entry + card_id = card['id'] + url = f"https://github.com/{GITHUB_OWNER}/{GITHUB_REPO}/projects/3#card-{card_id}" + instance = cls(author, text, url=url, updated_at=updated_at) + return instance + + @classmethod + def from_issue(cls, url, issue): + labels = [ + label['name'] + for label in issue['labels'] + ] + author = issue['assignee'] + if author: + author = author['login'] + if not author: + author = issue['user']['login'] + updated_at = issue['updated_at'][:-1] + updated_at = datetime.fromisoformat(updated_at) + text = issue['title'] + url = issue['html_url'] + instance = cls(author, text, labels, url, updated_at=updated_at) + return instance + + +if __name__ == '__main__': + main() diff --git a/.github/workflows/standup.yml b/.github/workflows/standup.yml index e34c0eb..6fe01de 100644 --- a/.github/workflows/standup.yml +++ b/.github/workflows/standup.yml @@ -12,14 +12,63 @@ jobs: standup: runs-on: ubuntu-latest permissions: + contents: write issues: write + pull-requests: write repository-projects: read steps: - run: | echo "Log level: ${{ github.event.inputs.logLevel }}" echo "Tags: ${{ github.event.inputs.tags }}" - curl \ - -H 'Accept: application/vnd.github.inertia-preview+json' \ - https://api.github.com/projects/columns/14068734/cards \ - ; -# -H 'authorization: Bearer ' \ + - uses: actions/checkout@v2 + - run: pwd + - run: ls + - run: ls -la + - run: pip install -r .github/requirements.txt + - run: | + export BASENAME="docs/standup/$(date '+%Y/%m')" + export DATE="$(date '+%Y/%m/%d')" + export FILENAME="$(date '+%d').markdown" + export BRANCHNAME="$(date '+%Y/%m/%d').markdown" + echo "BASENAME=${BASENAME}" >> "${GITHUB_ENV}" + echo "FILENAME=${FILENAME}" >> "${GITHUB_ENV}" + echo "BRANCHNAME=${BRANCHNAME}" >> "${GITHUB_ENV}" + echo "DATE=${DATE}" >> "${GITHUB_ENV}" + echo "::set-output name=BASENAME::${BASENAME}" + echo "::set-output name=FILENAME::${FILENAME}" + echo "::set-output name=BRANCHNAME::${BRANCHNAME}" + id: BASENAME + - run: mkdir -p ${{ steps.BASENAME.outputs.BASENAME }} || true + - run: python3 .github/standup.py > ${{ steps.BASENAME.outputs.BASENAME }}/${{ steps.BASENAME.outputs.FILENAME }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - run: cat ${{ steps.BASENAME.outputs.BASENAME }}/${{ steps.BASENAME.outputs.FILENAME }} + - uses: JasonEtco/create-an-issue@v2 + id: create + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + filename: ${{ steps.BASENAME.outputs.BASENAME }}/${{ steps.BASENAME.outputs.FILENAME}} + update_existing: true + - name: Create Pull Request + uses: peter-evans/create-pull-request@v3 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + token: "${{ secrets.GITHUB_TOKEN }}" + branch: standup/${{ env.DATE }} + commit-message: | + standup: ${{ env.DATE }} + title: | + standup: ${{ env.DATE }} + body: | + This is the day's standup. + + Closes #${{ steps.create.outputs.number }} + labels: standup + committer: "GitHub " + author: "GitHub " + # assignees: one, two + # reviewers: one, two + # team-reviewers: one, two + # milestone: mklasdfas diff --git a/docs/standup/2021/05/01.markdown b/docs/standup/2021/05/01.markdown new file mode 100644 index 0000000..147097f --- /dev/null +++ b/docs/standup/2021/05/01.markdown @@ -0,0 +1,25 @@ +--- +title: 'standup: 2021-05-04' +assignees: stvstnfrd +labels: standup +--- +# [standup: 2021-05-04](https://github.com/stvstnfrd/openedx-meta-meta/issues?q=is%3Aissue+sort%3Aupdated-desc) + +## [done](https://github.com/stvstnfrd/openedx-meta/projects/3#column-14068734) + +### [@stvstnfrd](https://github.com/stvstnfrd/openedx-meta/issues/assigned/stvstnfrd) + +- [x] [feat: Add extra legacy discussions settings handling [CENG-46]](https://github.com/stvstnfrd/openedx-meta/issues/2) +- [x] [Do a thing](https://github.com/stvstnfrd/openedx-meta/projects/3#card-60152620) + +## [doing](https://github.com/stvstnfrd/openedx-meta/projects/3#column-14068727) + +### [@stvstnfrd](https://github.com/stvstnfrd/openedx-meta/issues/assigned/stvstnfrd) + +- [ ] [test: transitions [CENG-45]](https://github.com/stvstnfrd/openedx-meta/issues/88) + +## [todo](https://github.com/stvstnfrd/openedx-meta/projects/3#column-14068716) + +### [@stvstnfrd](https://github.com/stvstnfrd/openedx-meta/issues/assigned/stvstnfrd) + +- [ ] [discuss: a thing](https://github.com/stvstnfrd/openedx-meta/projects/3#card-60157902)