gitar

And we're almost ready in the backend:

Currently, it's possible to view a directory's contents, the URL's are working properly (although I'd love to remove the trailing slash, but I'll look into that when the rest is on point), and I'm able to parse a file's contents to HTML formatted code. So what's next? Possibly a collection of the HTML templates I use, which I will be working on tomorrow. And also a series of CSS files that provide the right colors for the syntax. I think I've got enough to do tomorrow, but I'm also convinced that I might get a stable version by then. Not to forget: Tomorrow will be the time for writing tests. I hope I can come up with some more than I currently have. If I feel like it, I may also do some code cleanup. But that's all for tomorrow.

Author
Vngngdn
Date
Sept. 9, 2016, 12:16 a.m.
Hash
0d2a21c9e999ed1003cb6d81a1fcc0aece78bd44
Parent
e0aa7f60599b0d86debc84f0c2015cda3e40f270
Modified files
syntax.py
views.py

syntax.py

38 additions and 0 deletions.

View changes Hide changes
1
1
2
2
In essence, this means that in this module, a file's contents are being parsed
3
3
to Pygments, which will then return the appropriate HTML output, which can then
4
4
be directly parsed in a Django template.
5
5
"""
6
6
7
7
from pygments import highlight
+
8
from pygments.lexers import get_lexer_by_name
+
9
from pygments.formatters import HtmlFormatter
+
10
from pygments.lexers import guess_lexer_for_filename
+
11
+
12
def code_to_HTML(code, file_name):
+
13
    """ Turns the given list of code strings in HTML, ready for output.
+
14
+
15
    Please note that the lexer that will be used for the syntax coloring, is
+
16
    determined by the given file name, so assert that the given code comes from
+
17
    the given file.
+
18
    Keyword arguments:
+
19
    code -- A non-empty list of code strings.
+
20
    file_name -- The name of the file from where the given code originates.
+
21
    """
+
22
    lexer = guess_lexer_for_filename(file_name, code, stripall=True)
+
23
    # linenos (line-n°'s) adds line numbering to the front of the output. I'm
+
24
    # doing that myself, so False.
+
25
    # cssclass sets the enclosing <div>'s class to the given CSS class name.
+
26
    formatter = HtmlFormatter(linenos=False, cssclass="source")
+
27
    #result = highlight(code, lexer, formatter)
+
28
    unicode_data = decode_to_unicode(highlight(code, lexer, formatter))
+
29
    return unicode_data
+
30
+
31
def decode_to_unicode(data):
+
32
    """ Decodes a given bytearray to a list of unicode strings."""
+
33
    #decoded_data = data.decode("utf-8")
+
34
    decoded_data = data
+
35
    formatted_data = []
+
36
    line = ""
+
37
    for character in decoded_data:
+
38
        if character != "\n":
+
39
            line = line + character
+
40
        else:
+
41
            formatted_data.append(line)
+
42
            line = ""
+
43
    return formatted_data
+
44
+
45

views.py

11 additions and 1 deletion.

View changes Hide changes
1
1
    Copyright © 2016 Maarten "Vngngdn" Vangeneugden
2
2
3
3
    This program is free software: you can redistribute it and/or modify
4
4
    it under the terms of the GNU Affero General Public License as
5
5
    published by the Free Software Foundation, either version 3 of the
6
6
    License, or (at your option) any later version.
7
7
8
8
    This program is distributed in the hope that it will be useful,
9
9
    but WITHOUT ANY WARRANTY; without even the implied warranty of
10
10
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
11
    GNU Affero General Public License for more details.
12
12
13
13
    You should have received a copy of the GNU Affero General Public License
14
14
    along with this program. If not, see https://www.gnu.org/licenses/agpl.html.
15
15
"""
16
16
17
17
from django.shortcuts import get_object_or_404, render  # This allows to render the template with the view here. It's pretty cool and important.
18
18
from django.http import HttpResponseRedirect, HttpResponse 
19
19
from django.core.urlresolvers import reverse 
20
20
from .models import *
21
21
22
22
from .GitActions import RepoInfo
23
23
24
24
from git import Repo  # GitPython functionality.
25
25
import git
26
26
27
27
# First, I list some standard variables that are common for most of the sites of this app.
+
28
+
29
# First, I list some standard variables that are common for most of the sites of this app.
28
30
29
31
def footer_description():
30
32
    return "Gitar is a simple web app that allows its users to easily share Git repos in combination with the Django framework."
31
33
32
34
def footer_links():
33
35
    footer_links = [
34
36
            ['Source', 'OHGODHELPNOTDONEYET'],
35
37
            ['Personal website', reverse('about-index')],
36
38
            ]
37
39
    return footer_links
38
40
39
41
def standard_context():
40
42
    context = {
41
43
            'materialDesign_color': "blue-grey",
42
44
            'materialDesign_accentColor': "green",
43
45
            'navbar_title': "Gitar",
44
46
            'navbar_backArrow': False,
45
47
            'footer_title': "Gitar",
46
48
            'footer_description': footer_description(),
47
49
            'footer_links': footer_links(),
48
50
            }
49
51
    return context
50
52
51
53
# From here, the actual views start.
52
54
def index(request):
53
55
    """ The start page of Gitar.
54
56
55
57
    The goal of this view, is to collect all the available repositories,
56
58
    including some additional information, such as programming language,
57
59
    license, description, ... in order to give a fast overview of the most
58
60
    prominent information.
59
61
    """
60
62
61
63
    # Collecting the available repositories:
62
64
    # Template:
63
65
    template = "gitar/index.html"
64
66
    # Requesting the repositories:
65
67
    modelRepos = Repository.objects.all()
66
68
    # From here, we start collecting info about all the repositories:
67
69
    class BlankRepository: pass  # Blank object in which all data will be collected.
68
70
    repositories = []
69
71
    for modelRepo in modelRepos:
70
72
        repository = BlankRepository()
71
73
        # TODO: Find a way to add all of modelRepo's fields without having to
72
74
        # hardcode them. This is prone to errors and is redundant.
73
75
        repository.name = str(modelRepo)
74
76
        repository.programmingLanguage = modelRepo.programmingLanguage
75
77
        repository.license = modelRepo.license
76
78
        repository.description = RepoInfo.get_description(modelRepo)
77
79
78
80
        #gitRepo = Repo.init(modelRepo.directory(), bare=True)  # Connects to the Git Repo.
79
81
        # See tests.py, which assures all repositories exist. Tests are handy.
80
82
        #repository.description = gitRepo.description
81
83
        # This is mostly personal taste, but I like to show the amount of files.
82
84
        #repoTree = gitRepo.heads.master.commit.tree
83
85
        #repository.fileCount = len(repoTree.blobs)  # blobs are files.
84
86
        repositories.append(repository)
85
87
    # After that, I extend the standard context with the repositories:
86
88
    context = standard_context()
87
89
    context['repositories'] = repositories
88
90
    # And finally, sending everything back.
89
91
    return render(request, template, context)
90
92
91
93
def repositories(request, repository_name):
92
94
    # I first form the repository link out the given name.
93
95
    repository = RepoInfo.get_repository_object(repository_name)
94
96
    assert repository.bare  # My own repos are always bare.
95
97
    content = repository.heads.master.commit.tree
96
98
97
99
    # Out of which I can deduce the files:
98
100
    files = content.blobs
99
101
    fileNames = []
100
102
    for filee in files:
101
103
        fileNames.append(filee.name)
102
104
    template = "gitar/repositories.html"
103
105
    context = standard_context()
104
106
    context['files'] = fileNames
105
107
106
108
    assert len(content.blobs) > 0
107
109
    assert len(fileNames) > 0
108
110
    return render(request, template, context)
109
111
110
112
def path_explorer(request, repository_name, path):
111
113
    """ Checks whether the given path is a file or a directory, and calls the
112
114
    appropriate view function accordingly.
113
115
    """
114
116
    repository = RepoInfo.get_repository_object(repository_name)
115
117
    # From the GitPython documentation:
116
118
    # You can obtain the tree object of a repository, which is the directory of
117
119
    # that repo. This tree can be accessed as if it were a native Python list,
118
120
    # where the elements are the subdirectories and files. So, the idea to
119
121
    # determine whether a file, or a directory was requested, is simple:
120
122
    # 1. Split the path with "/" as seperator.
121
123
    # 2. Replace the current tree variable with the one retrieved from the
122
124
    # subtree element
123
125
    # 3. Repeat 2. until all parts of the given path are exhausted.
124
126
    # If we now still have a tree, we're looking at a directory, so display the
125
127
    # files (and subdirectories) of this directory.
126
128
    # Else, if we hit a blob, display the file contents.
127
129
    path_parts = path.split(sep="/")
128
130
    # FIXME: If the user gives a "<something>/../<somethingElse>", that should
129
131
    # become "<something>". Obviously, although I think that's done by default
130
132
    # already.
131
133
    directory = repository.heads.master.commit.tree
132
134
    for i in range(len(path_parts)-1):
133
135
        subdirectories = directory.trees
134
136
        if len(subdirectories) == 0:
135
137
            # If there's no more subdirectory, and we get here, then that means
136
138
            # it's a file for sure, because otherwise, the iterator would've
137
139
            # stopped.
138
140
            blobs = directory.blobs
139
141
            for blob in directory.blobs:
140
142
                if blob.name == path_parts[i]:
141
143
                    file_blob = blob
142
144
                    return file_view(request, repository_name, path, file_blob)
143
145
        else:
144
146
            for subdirectory in subdirectories:
145
147
                if subdirectory.name == path_parts[i]:
146
148
                    directory = subdirectory
147
149
                    break
148
150
    # Current state:
149
151
    # The entire path was traversed, and we kept reaching subdirectories, which
150
152
    # means that the current directory was requested, and not a file. Thus,
151
153
    # display the directory.
152
154
    return directory_view(request, repository_name, path, directory)
153
155
154
156
def directory_view(request, repository_name, path, directory):
155
157
    """ Collects the given directories's files and subdirectories, and renders a
156
158
    template to display this data.
157
159
    """
158
160
    # Collecting all files:
159
161
    files = directory.blobs
160
162
    file_names = []
161
163
    for ffile in files:
162
164
        file_names.append(ffile.name)
163
165
    # Collecting all subdirectories:
164
166
    subdirectories = directory.trees
165
167
    subdirectory_names = []
166
168
    for subdirectory in subdirectories:
167
169
        subdirectory_names.append(subdirectory.name)
168
170
    # Collecting rendering information:
169
171
    template = "gitar/directory.html"
170
172
    context = standard_context()
171
173
    context["files"] = file_names
172
174
    context["subdirectories"] = subdirectory_names
173
175
    return render(request, template, context)
174
176
175
177
176
178
def file_view(request, repository_name, path, ffile):
177
179
    """ Collects the file contents of the given file path, and returns it to the
178
180
    template, with the file contents already formatted in HTML using Pygments.
179
181
    """
180
182
181
183
    file_content = RepoInfo.read_file(ffile)
182
-
+
184
    raw_file_data = ffile.data_stream.read()
+
185
    html_code = code_to_HTML(raw_file_data, ffile.name)
+
186
    # Collecting rendering information:
+
187
    template = "gitar/file.html"
+
188
    context = standard_context()
+
189
    context["content"] = html_code
+
190
    return render(request, template, context)
+
191
    
+
192