Ejemplo n.º 1
0
def _mark_todo(notes_directory, note_path, line_number, status):

    full_path = get_full_path(notes_directory, note_path)

    with open(full_path, 'r') as file_object:
        file_content = file_object.read()

    split_content = file_content.split('\n')
    line_content = split_content[line_number - 1]

    # Modify line_content
    block_pattern = r'(^\s*)(\[)([ XS]*)(\])'
    block_regex = re.compile(block_pattern)

    if status == 'complete':
        sub_character = 'X'
    elif status == 'skipped':
        sub_character = 'S'
    else:
        sub_character = ' '

    line_content = block_regex.sub('\\g<1>[{}]'.format(sub_character),
                                   line_content)

    split_content[line_number - 1] = line_content
    with open(full_path, 'w') as file_object:
        file_object.write('\n'.join(split_content))

    return line_content
Ejemplo n.º 2
0
def _get_record_set(notes_directory,
                    file_path,
                    line_number,
                    parse=True,
                    parse_format='json',
                    include_config=False):
    '''Get the full contents of a record set
    If `parse` is set to False then the record set
        contents are returned as a string
    If `parse` is set to True then the record set
        contents are loaded and returned in the specified
        format. Valid formats are `json` and `csv`
    '''

    # Validate inputs
    if parse and parse_format not in ['json', 'csv']:
        raise ValueError(f'Unknown parse format {parse_format}')

    # Get full path if only a relative path is supplied
    if notes_directory not in file_path:
        file_path = get_full_path(notes_directory, file_path)

    record_set_lines = []
    is_content = False
    with open(file_path, 'r') as f:
        file_data = f.read()
    for idx, line in enumerate(file_data.split('\n')):
        if idx + 1 < line_number:
            continue
        elif idx + 1 == line_number and line != '```rec-data':
            raise ValueError(f'Found unexpected line "{line}"')
        elif idx + 1 == line_number and line == '```rec-data':
            is_content = True
        elif is_content and line != '```':
            record_set_lines.append(line)
            continue
        elif is_content and line == '```':
            is_content = False
            break
        else:
            raise ValueError('Found a bug in record set parsing logic!')
    record_set_raw = '\n'.join(record_set_lines)
    if parse:
        record_set = load_from_string(record_set_raw)
        if parse_format == 'json':
            if include_config:
                output = {
                    'records': list(record_set.all()),
                    'config': record_set.get_config(),
                    'fields': record_set.get_fields()
                }
                return output
            output = record_set.get_json()
            return output
        elif parse_format == 'csv':
            output = record_set.get_csv()
            return output
    else:
        return record_set_raw
Ejemplo n.º 3
0
def _delete_note(notes_directory, note_path):
    '''Deletes a note from the filesystem
    '''
    full_path = get_full_path(notes_directory, note_path)

    if not os.path.exists(full_path):
        raise ValueError(f'Note to delete at path {note_path} does not exist')

    os.remove(full_path)
Ejemplo n.º 4
0
def is_image_path(notes_directory, path):
    # Check the file extension
    extension = path.split('.')[-1]
    if extension not in IMAGE_FILE_EXTENSIONS:
        return False

    # Check that the specified file actually exists
    full_path = get_full_path(notes_directory, path)
    if not os.path.exists(full_path):
        return False

    return True
Ejemplo n.º 5
0
    def get_links(self,
                  notes_directory,
                  source=None,
                  target=None,
                  note=None,
                  include_external=False,
                  include_invalid=False,
                  grep_path=None):

        links = ALL_LINKS

        # Filter for source
        if source:
            links = [link for link in links if link['source'] == source]

        # Filter for target
        if target:
            links = [
                link for link in links
                if link['target'].split('#')[0] == target
            ]

        # Filter for note
        if note:
            links = [
                link for link in links if link['target'].split('#')[0] == note
                or link['source'] == note
            ]

        # Filter for internal only vs. external
        if not include_external:
            links = [link for link in links if link['target'][0] == '/']

        # Split into valid and invalid links
        for link in links:
            target_full_path = get_full_path(notes_directory,
                                             link['target'].split('#')[0])
            target_exists = os.path.exists(target_full_path)
            if link['target'][0] != '/' or target_exists:
                link['valid'] = True
            else:
                link['valid'] = False

            # Target is outside the notes directory
            if notes_directory not in target_full_path:
                link['valid'] = False

        # Filter for valid internal targets
        if not include_invalid:
            links = [link for link in links if link['valid']]

        return links
Ejemplo n.º 6
0
def _update_note(notes_directory, file_path, content):
    '''Update an existing note with the full contents provided
    '''

    # Ensure that we have the full path even if
    # a relative path is specified
    full_path = get_full_path(notes_directory, file_path)

    if not os.path.exists(full_path):
        raise ValueError(f'Note to get at path {file_path} does not exist')

    with open(full_path, 'w') as note_file:
        note_file.write(content)
Ejemplo n.º 7
0
def _create_note(notes_directory, note_path, content=None):
    '''Create a new note
    '''
    if not note_path:
        raise ValueError('No note path provided for new note to create')

    full_path = get_full_path(notes_directory, note_path)

    if os.path.exists(full_path):
        raise ValueError(f'Note to create at path {note_path} already exists')

    with open(full_path, 'w') as f:
        if content:
            f.write(content)
Ejemplo n.º 8
0
def _append_to_note(notes_directory, note_path, content, blank_lines=1):
    '''Append the specified content to an existing note
    '''
    full_path = get_full_path(notes_directory, note_path)

    if not os.path.exists(full_path):
        raise ValueError(f'Note to append to at path {note_path} does '
                         f'not exist')

    if blank_lines:
        content = ('\n' * (blank_lines + 1)) + content

    with open(full_path, 'a') as note_file:
        note_file.write(content)
Ejemplo n.º 9
0
def _get_note(notes_directory, path):
    '''Get the full raw content of a note as a string
    given:
        - The full path to the notes directory
        - Its path, as a relative path
          within the notes directory
    '''

    full_path = get_full_path(notes_directory, path)

    if not os.path.exists(full_path):
        raise ValueError(f'Note to get at path {path} does not exist')

    with open(full_path, 'r') as note_file_object:
        note_content = note_file_object.read()

    return note_content
Ejemplo n.º 10
0
def _get_links(notes_directory, source=None, target=None, note=None,
               include_external=False, include_invalid=False,
               grep_path='grep'):
    '''Get all links between notes within the notes directory

       notes_directory: The directory to do the search within
       source: Only return links where the specified note is the source of
               the link
       target: Only return links where the specified note is the target of
               the link
       note: Only return links where the specified note is either the source or
             the target of the link
       include_external: Boolean for whether or not external links are included
       include_invalid: Boolean for whether or not links with invlaid targets
                        are included
       grep_path: Path to the grep CLI utility to use for the search

       Returns:
            [
                {
                    'line_number': '26',
                    'source': '/section/mixed.note',
                    'target': '/todos.note',
                    'text': 'todos',
                    'internal': true,
                    'valid': true
                }, {
                    'line_number': '26',
                    'source': '/section/mixed.note',
                    'target': '/questions.note',
                    'text': 'questions',
                    'internal': true,
                    'valid': true
                }
            ]
    '''

    if note:
        if source or target:
            raise ValueError('Parameter `note` cannot be combined '
                             'with `source` or `target`')

        # Get the set of all links where the specified note is either
        # the source or the target
        source_links = _get_links(notes_directory=notes_directory, source=note,
                                  target=None, note=None,
                                  include_external=include_external,
                                  include_invalid=include_invalid,
                                  grep_path=grep_path)
        target_links = _get_links(notes_directory=notes_directory, source=None,
                                  target=note, note=None,
                                  include_external=include_external,
                                  include_invalid=include_invalid,
                                  grep_path=grep_path)

        all_links = source_links + target_links
        all_links = deduplicate_links(all_links)

        return all_links

    links = []

    if target:
        target_filename = os.path.basename(target)

    LINK_PATTERN = r'(\[)([^\[]*?)(\]\()'
    if target:
        # Only include the target filename to catch both
        # relative and absolute references
        LINK_PATTERN += rf'(.*?{target_filename})(#.+?)?'
    elif not include_external:
        # Only catch internal links which don't have http[s]://
        LINK_PATTERN += r'((?!(https://|http://)).*?)'
    else:
        # Catch everything
        LINK_PATTERN += r'(.*?)'
    LINK_PATTERN += r'(\))'

    if source:
        search_path = get_full_path(notes_directory, source)
    else:
        search_path = notes_directory

    # Use Grep to find all links
    grep_command = '{grep_path} -Prn "{pattern}" '\
                   '--include="*.note" --exclude-dir=\'.*\' {dir}'.format(
                        grep_path=grep_path,
                        pattern=LINK_PATTERN,
                        dir=search_path)
    log.debug(f'Running grep command {grep_command} to get links')

    proc = Popen(
        grep_command,
        stdout=PIPE, stderr=PIPE,
        shell=True)
    output, err = proc.communicate()
    output_lines = output.decode().split('\n')

    for line in output_lines:

        log.debug(f'Got line "{line}"')

        if not line.strip():
            continue

        # Grep returns results in different forms depending on if you specify
        # a path to a file or directory
        #     dir:  <file-path>:<line-number>:<line>
        #     file: <line-number>:<line>
        if source:
            split_line = line.split(':', 1)

            file_path = source
            line_number = split_line[0].strip()
            match_content = split_line[1].strip()

        else:
            split_line = line.split(':', 2)

            file_path = split_line[0].strip()
            line_number = split_line[1].strip()
            match_content = split_line[2].strip()

        note_path = get_relative_path(notes_directory, file_path)

        matches = link_regex.findall(match_content)
        for match in matches:

            # The matching group for the text starts
            # with `[` and ends with `](`
            link_text = match[0][1:-2]
            link_target = match[1]

            link_target = parse_relative_link_path(source=note_path,
                                                   target=link_target)
            link_target_file = link_target.split('#')[0]
            is_external_link = False
            is_valid_link = True

            # Sanity check the link target which Grep should have already
            # filtered for
            if target and link_target != target:
                if '#' in link_target and link_target_file == target:
                    log.debug(f'Found link to subsection {link_target}')
                else:
                    log.debug(f'Found unexpected target {link_target}')
                    continue

            is_external_link = is_external_path(link_target)
            if is_external_link and not include_external:
                continue

            if not is_external_link:
                is_valid_link = is_note_path(notes_directory, link_target_file)
                if not include_invalid and not is_valid_link:
                    log.info(f'Skipping invalid link to {link_target}')
                    continue

            link = {
                'line_number': line_number,
                'source': note_path,
                'target': link_target,
                'text': link_text,
                'internal': not is_external_link,
                'valid': is_valid_link
            }

            links.append(link)

    return links