Skip to content

Commit

Permalink
DIST: releases v1.0.0
Browse files Browse the repository at this point in the history
V 1.0.0
  • Loading branch information
andre-filho authored Aug 10, 2019
2 parents 237398c + 215156a commit d3c194e
Show file tree
Hide file tree
Showing 9 changed files with 287 additions and 53 deletions.
4 changes: 4 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,7 @@ deploy:
script: bash deploy.sh deploy
on:
branch: master
- provider: script
script: bash deploy.sh test
on:
branch: development
34 changes: 34 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,12 @@
<img src="https://api.codacy.com/project/badge/Coverage/595af9a088cf44e19ec2679a8c2617f6"/>
</a> -->
</p>
## Table of contents

[TOC]

## What does it do?

The **ezissue cli** is an application with command line interface which it's main objective is to help you
in the issue creation process in your projects.

Expand All @@ -34,6 +38,8 @@ But if you want to spend hours creating issues on Github or Gitlab and find it f

## Usage and configuration

### CLI interface

This program has a CLI that you can take advantage of. Running `ezissue --help`
will show you the usage and options for the CLI.

Expand All @@ -49,20 +55,48 @@ $ ezissue --help
--help Show this message and exit.
```

### Markdown file and configuration

The EZIssue program takes a `.md` file as argument. That file must have a markdown table for it to parse to issues. That table is a common md table and can have the following headers: (Note that headers with `*` are mandatory)

| **Header name** | Description | Github support | Gitlab support |
| ------------------- | :----------------------------------------------------------- | :-------------------------------: | :----------------------------------------------------------: |
| Title* | Issue’s title | Y | Y |
| Description | Issue’s body or description | Y | Y |
| Tasks | Will be a list of checkboxes. Items must be separated with commas. | Y (goes with description) | Y (goes with description) |
| Acceptance criteria | Will be a list of checkboxes. Items must be separated with commas. | Y (goes with description) | Y (goes with description) |
| Assignee | User that is assigned to the issue | Y (assignee’s username) | N (see next row) |
| Assignees | List of users assigned to the issue | Y (array of assignee’s usernames) | Y (is a array of user ids) |
| Labels | List of labels that are to be applied to the issue | Y (array of strings) | Y (single string, separated by commas) |
| Confidential | Toggles the confidentiality of the issue | N | Y (boolean value) |
| Milestone | Adds a milestone to the issue | Y (number of milestone) | Y (milestone id) |
| Due | Sets a due date for stressing out your team | N | Y (datetime string in format `YYYY-MM-DD`) |
| Discussion | Links the issue to a discussion thread | N | Y (id of the discussion that it solves. Fills the description automatically) |
| Weight | Sets the issue’s weight. Best used in XP | N | Y (integer with the issue’s weight, must be bigger than zero) |

**Examples:**

The issue output format is the following:

```markdown
<!-- issue-table.md -->
| title | description | acceptance criteria |
| ----- | ----------- | ------------------- |
| issue title | brief description | condition a;condition b;condition c |
```



```markdown
<!--title-->
<PREFIX><SUBID><NUMBER> issue title
<!--body-->
**Issue description:**
---
brief description

**Acceptance criteria:**
---
- [ ] condition a
- [ ] condition b
- [ ] condition c
Expand Down
2 changes: 1 addition & 1 deletion VERSION.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0.2.2
0.2.3
89 changes: 81 additions & 8 deletions ezissue/converter/manipulation.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import re


def remove_md_titles(line, file):
def remove_md_titles(line):
line = re.sub(
r'((#\s)|(##\s)|(###\s)|(####\s)|(#####\s)|(######\s))',
'',
Expand All @@ -11,39 +11,112 @@ def remove_md_titles(line, file):


def md_table_row_to_array(line):
line = re.sub(r'(\ \|\ )', '-', line)
"""
Recieves a table line and parses it's content as a array of strings, with
each element representing a column.
"""
# replaces the | characters between the text with a totally random string
line = re.sub(r'(\ \|\ )', '3Vb7adZJNY', line)
line = re.sub(r'(\|\ )|(\ \|)', '', line)
line = line.replace("\\n", '')
line = line.split('-')
line = line.replace("\n", '')
line = line.split('3Vb7adZJNY')
return line


def add_md_checkbox(items):
items = items.split(';')
def add_md_checkbox(itemized_string):
"""
Converts a itemized string in a formatted markdown checkbox list.
"""
items = itemized_string.split(';')
a = ""
for item in items:
a += str('- [ ] ' + item + '\n')
return a


def format_description(description):
return str('**Issue description:**\n' + description + '\n')
def format_description(string):
"""
Adds the header and a final new line to the issue description string.
"""
return str('**Issue description:**\n---\n\n' + string + '\n'*2)


def add_prefix_to_title(title, number, prefix, subid, numerate):
"""
Formats the issue title to match the required for the project.
"""
subid = subid.upper()
prefix = prefix.upper()
title = title.capitalize()

if numerate:
return str(prefix + subid + str(number) + " " + title)

return str(prefix + subid + " " + title)


def get_all_lines(file):
"""
Reads a file returning all it's lines in a array of lines format.
"""
line = file.readline()
lines = []

while line:
lines.append(line)
line = file.readline()

return lines


def get_table_spec(line):
"""
Gets the table header and returns both it's length and it's formatted self.
"""
thead = md_table_row_to_array(line)
return len(thead), thead


def format_acc_criteria(string):
"""
Formats the string adding the acceptance criteria header and adds a final
new line.
"""
checkboxes = add_md_checkbox(string)
acc_criteria_title = "**Acceptance criteria:**\n---\n\n"

return "%s%s" % (acc_criteria_title, checkboxes)


def format_tasks(string):
"""
Formats the string adding the tasks header and adds a final new line.
"""
checkboxes = add_md_checkbox(string)
tasks_title = "**Tasks:**\n---\n\n"
return "%s%s\n" % (tasks_title, checkboxes)


def make_md_formatting(configuration_header, content):
"""
Dinamically formats the markdown content according to it's column header.
"""
func_dict = {
"description": format_description,
"body": format_description,
"acceptance criteria": format_acc_criteria,
"tasks": add_md_checkbox,
}

cont_length = len(content)
conf_header_length = len(configuration_header)

for row in range(cont_length):
for idx in range(conf_header_length):
if len(configuration_header) == 0 or idx == 0:
pass
else:
content[row][idx] = \
func_dict[configuration_header[idx].lower()](
str(content[row][idx]))
return content
109 changes: 83 additions & 26 deletions ezissue/ezissue.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,33 +16,76 @@
GITLAB_BASE_URL = "https://gitlab.com/api/v4"


def create_issue_json(title, description, acceptance_criteria, repo_host):
body = "%s\n%s" % (description, acceptance_criteria)

if repo_host == 'gitlab':
return {"title": title, "description": body}

return {"title": title, "body": body}
def create_issue_json(configuration_row, values_row, repo_host):
"""
Creates a python dict with the issue's values following the format
dict({<table header>: <row value>}).
"""
n_fields = len(configuration_row)
d = dict()

blacklist = ["acceptance criteria", "tasks"]

if n_fields != len(values_row):
u.error(
'Error: markdown table header and contents columns do not match!')
raise

for idx in range(n_fields):
configuration_row[idx] = configuration_row[idx].lower()

if configuration_row[idx] == 'title':
d.update({'title': values_row[idx]})

elif configuration_row[idx] == 'description' and repo_host == 'github':
d.update({'body': values_row[idx]})

elif configuration_row[idx] == 'body' and repo_host == 'gitlab':
d.update({'description': values_row[idx]})

elif configuration_row[idx] in blacklist:
if repo_host == 'gitlab':
body = d['description']
body = body + str(values_row[idx])
d.update({'description': body})
else:
body = d['body']
body = body + str(values_row[idx])
d.update({'body': body})
else:
d.update({configuration_row[idx]: values_row[idx]})
return d


def create_github_url(repo_name, owner):
"""
Creates the github's issue endpoint URL for accessing the API.
"""
github = "/repos/%s/%s/issues" % (owner.lower(), repo_name.lower())
endpoint = GITHUB_BASE_URL + github
return endpoint


def create_gitlab_url(repo_id):
gitlab = "/projects/%i/issues" % repo_id
def create_gitlab_url(repo_uid):
"""
Creates the gitlab's issue endpoint URL for accessing the API.
"""
gitlab = "/projects/%i/issues" % repo_uid
endpoint = GITLAB_BASE_URL + gitlab
return endpoint


def make_api_call(json_issue, url, host, debug):
"""
Makes the API POST request for the issue creation.
Returns the response object and the issue's dict.
"""
u.debug(json_issue, debug)

my_token = get_token(host)

if not host == 'github':
a = requests.post(
response = requests.post(
url,
data=js.dumps(json_issue),
headers={
Expand All @@ -52,7 +95,7 @@ def make_api_call(json_issue, url, host, debug):
)
else:
auth = 'Bearer %s' % my_token
a = requests.post(
response = requests.post(
url,
data=js.dumps(json_issue),
headers={
Expand All @@ -61,7 +104,7 @@ def make_api_call(json_issue, url, host, debug):
'Content-Type': 'application/json'
}
)
return a, json_issue
return response, json_issue


@click.command()
Expand Down Expand Up @@ -105,6 +148,9 @@ def make_api_call(json_issue, url, host, debug):
help='Enables debug mode'
)
def main(filename, repo_host, prefix, subid, numerate, debug):
"""
Main function.
"""
if not os.path.isfile(folder_path + 'key.key'):
config()

Expand All @@ -113,14 +159,13 @@ def main(filename, repo_host, prefix, subid, numerate, debug):
lines = get_all_lines(file)
rows = []

for line in lines:
rows.append(md_table_row_to_array(line))

for idx, row in enumerate(rows):
row[0] = add_prefix_to_title(
row[0], idx+1, prefix, subid, numerate)
row[1] = format_description(row[1])
row[2] = add_md_checkbox(row[2])
for idx, line in enumerate(lines):
if idx == 0:
col_count, columns = get_table_spec(line)
elif idx == 1:
pass
else:
rows.append(md_table_row_to_array(line))

if repo_host == 'github':
repo = u.get_from_user("Enter repo name: (Ex.: username/repo)")
Expand All @@ -132,28 +177,40 @@ def main(filename, repo_host, prefix, subid, numerate, debug):

u.debug(repr(url), debug)
u.debug(repr(repo_host), debug)
responses = []

for idx, row in enumerate(rows):
row[0] = add_prefix_to_title(
row[0], idx+1, prefix, subid, numerate)

rows = make_md_formatting(columns, rows)

for row in rows:
req_resp, req_json = make_api_call(
create_issue_json(row[0], row[1], row[2], repo_host),
response, issue = make_api_call(
create_issue_json(columns, row, repo_host),
url,
repo_host,
debug
)
u.show_resp_req(req_json, req_resp)
u.show_resp_req(issue, response)

finally:
file.close()


def config():
"""
Runs setup configuration on the CLI. Creates a hidden folder on the user's
HOMEDIR with secure encrypted info.
"""
u.notify("Config file not found! Initializing configuration...")

ghtk = getpass.getpass(prompt="Please insert your github token: ")
gltk = getpass.getpass(prompt="Please insert your gitlab token: ")

create_secure_key()
a = write_tokens(ghtk, gltk)
if a:

success = write_tokens(ghtk, gltk)
if success:
u.prompt("Created config files successfully!")
u.prompt("(They're encrypted, don't worry)")
else:
Expand Down
Loading

0 comments on commit d3c194e

Please sign in to comment.