gitar

Found a way to read a file's contents, and (even better) turn the content in a list of unicode strings. Also, added the new syntax.py file, which will be the module for the Pygments interaction, which should go like a breeze, considering what I currently have.

Author
Vngngdn
Date
Sept. 8, 2016, 11:41 p.m.
Hash
e0aa7f60599b0d86debc84f0c2015cda3e40f270
Parent
7e5bcc2ab73c5a8b6e0fb387922703eb9e02fa0b
Modified files
GitActions/RepoInfo.py
syntax.py
urls.py
views.py

GitActions/RepoInfo.py

21 additions and 0 deletions.

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 ..models import Repository
18
18
import git
19
19
20
20
def get_description(repository):
21
21
    """ Returns the Git repo description of the given repository.
22
22
    """
23
23
    if isinstance(repository, Repository):
24
24
        repository = git.Repo(repository.directory_path)
25
25
26
26
    return repository.description
27
27
28
28
def get_repository_object(repository_name):
29
29
    """ Checks the database for a repository with the same name, and returns a
30
30
    GitPython Repo object.
31
31
32
32
    Given the name of the repository, this function will search the database for
33
33
    a repository whoms name corresponds with the given name. When it found one, 
34
34
35
35
    Keyword arguments:
36
36
    repository_name -- The name of the repository
37
37
    """
38
38
    # Next line raises a Repository.DoesNotExist exception if not found, so it's
39
39
    # not necessary to check whether it was found or not.
40
40
    repository = Repository.objects.get(directory_path__endswith=repository_name+".git")
41
41
42
42
    return git.Repo(repository.directory_path)
43
43
44
44
def get_repository_model_object(repository_name):
45
45
    """ Functions identical to the get_repository_object, except that this
46
46
    function returns the Django model representation.
47
47
48
48
    Keyword arguments:
49
49
    repository_name -- The name of the repository
50
50
    """
51
51
    return Repository.objects.get(directory_path__endswith=repository_name+".git")
52
52
+
53
def read_file(file_blob):
+
54
    """ Reads the contents of the given file, and returns it in a list of
+
55
    strings.
+
56
+
57
    Reading the contents of a file using GitPython is a bit cumbersome. This
+
58
    function takes care of the hassle, and returns a list of unicode strings,
+
59
    allowing easy operations on the file's contents.
+
60
    """
+
61
+
62
    file_data_stream = file_blob.data_stream
+
63
    file_content = file_data_stream.read().decode("utf-8")
+
64
    file_formatted_content = []
+
65
    line = ""
+
66
    for character in file_content:
+
67
        if character != "\n":
+
68
            line = line + character
+
69
        else:
+
70
            file_formatted_content.append(line)
+
71
            line = ""
+
72
    return file_formatted_content
+
73

syntax.py

7 additions and 0 deletions.

View changes Hide changes
+
1
+
2
In essence, this means that in this module, a file's contents are being parsed
+
3
to Pygments, which will then return the appropriate HTML output, which can then
+
4
be directly parsed in a Django template.
+
5
"""
+
6
+
7

urls.py

1 addition 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.conf.urls import url
18
18
19
19
from . import views # Imports the views from the same directory (which is views.py).
20
20
21
21
urlpatterns = [
22
22
        url(r'^$', views.index, name='gitar-index'),
23
23
        url(r'^(?P<repository_name>\w+)$', views.repositories, name='gitar-repository'),
24
24
        url(r'^(?P<repository_name>\w+)/(?P<path>\w+)$', views.path_explorer, name='gitar-path-explorer'),
25
-
+
25
26
26
        ]
27
27

views.py

16 additions and 8 deletions.

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
28
29
29
def footer_description():
30
30
    return "Gitar is a simple web app that allows its users to easily share Git repos in combination with the Django framework."
31
31
32
32
def footer_links():
33
33
    footer_links = [
34
34
            ['Source', 'OHGODHELPNOTDONEYET'],
35
35
            ['Personal website', reverse('about-index')],
36
36
            ]
37
37
    return footer_links
38
38
39
39
def standard_context():
40
40
    context = {
41
41
            'materialDesign_color': "blue-grey",
42
42
            'materialDesign_accentColor': "green",
43
43
            'navbar_title': "Gitar",
44
44
            'navbar_backArrow': False,
45
45
            'footer_title': "Gitar",
46
46
            'footer_description': footer_description(),
47
47
            'footer_links': footer_links(),
48
48
            }
49
49
    return context
50
50
51
51
# From here, the actual views start.
52
52
def index(request):
53
53
    """ The start page of Gitar.
54
54
55
55
    The goal of this view, is to collect all the available repositories,
56
56
    including some additional information, such as programming language,
57
57
    license, description, ... in order to give a fast overview of the most
58
58
    prominent information.
59
59
    """
60
60
61
61
    # Collecting the available repositories:
62
62
    # Template:
63
63
    template = "gitar/index.html"
64
64
    # Requesting the repositories:
65
65
    modelRepos = Repository.objects.all()
66
66
    # From here, we start collecting info about all the repositories:
67
67
    class BlankRepository: pass  # Blank object in which all data will be collected.
68
68
    repositories = []
69
69
    for modelRepo in modelRepos:
70
70
        repository = BlankRepository()
71
71
        # TODO: Find a way to add all of modelRepo's fields without having to
72
72
        # hardcode them. This is prone to errors and is redundant.
73
73
        repository.name = str(modelRepo)
74
74
        repository.programmingLanguage = modelRepo.programmingLanguage
75
75
        repository.license = modelRepo.license
76
76
        repository.description = RepoInfo.get_description(modelRepo)
77
77
78
78
        #gitRepo = Repo.init(modelRepo.directory(), bare=True)  # Connects to the Git Repo.
79
79
        # See tests.py, which assures all repositories exist. Tests are handy.
80
80
        #repository.description = gitRepo.description
81
81
        # This is mostly personal taste, but I like to show the amount of files.
82
82
        #repoTree = gitRepo.heads.master.commit.tree
83
83
        #repository.fileCount = len(repoTree.blobs)  # blobs are files.
84
84
        repositories.append(repository)
85
85
    # After that, I extend the standard context with the repositories:
86
86
    context = standard_context()
87
87
    context['repositories'] = repositories
88
88
    # And finally, sending everything back.
89
89
    return render(request, template, context)
90
90
91
91
def repositories(request, repositoryName):
92
-
    # I first form the repository link out the given name.
+
92
    # I first form the repository link out the given name.
93
93
    repository = RepoInfo.get_repository_object(repositoryName)
94
-
    assert repository.bare  # My own repos are always bare.
+
94
    assert repository.bare  # My own repos are always bare.
95
95
    content = repository.heads.master.commit.tree
96
96
97
97
    # Out of which I can deduce the files:
98
98
    files = content.blobs
99
99
    fileNames = []
100
100
    for filee in files:
101
101
        fileNames.append(filee.name)
102
102
    template = "gitar/repositories.html"
103
103
    context = standard_context()
104
104
    context['files'] = fileNames
105
105
106
106
    assert len(content.blobs) > 0
107
107
    assert len(fileNames) > 0
108
108
    return render(request, template, context)
109
109
110
110
def path_explorer(request, repository_name, path):
111
111
    """ Checks whether the given path is a file or a directory, and calls the
112
112
    appropriate view function accordingly.
113
113
    """
114
114
    repository = RepoInfo.get_repository_object(repository_name)
115
115
    # From the GitPython documentation:
116
116
    # You can obtain the tree object of a repository, which is the directory of
117
117
    # that repo. This tree can be accessed as if it were a native Python list,
118
118
    # where the elements are the subdirectories and files. So, the idea to
119
119
    # determine whether a file, or a directory was requested, is simple:
120
120
    # 1. Split the path with "/" as seperator.
121
121
    # 2. Replace the current tree variable with the one retrieved from the
122
122
    # subtree element
123
123
    # 3. Repeat 2. until all parts of the given path are exhausted.
124
124
    # If we now still have a tree, we're looking at a directory, so display the
125
125
    # files (and subdirectories) of this directory.
126
126
    # Else, if we hit a blob, display the file contents.
127
127
    path_parts = path.split(sep="/")
128
128
    # FIXME: If the user gives a "<something>/../<somethingElse>", that should
129
129
    # become "<something>". Obviously, although I think that's done by default
130
130
    # already.
131
131
    directory = repository.heads.master.commit.tree
132
132
    for i in range(len(path_parts)):
133
-
        subdirectories = directory.trees
+
133
        subdirectories = directory.trees
134
134
        if len(subdirectories) == 0:
135
135
            # If there's no more subdirectory, and we get here, then that means
136
136
            # it's a file for sure, because otherwise, the iterator would've
137
137
            # stopped.
138
138
            file_blob = directory.blobs[path_parts[i]]
139
-
            return file_viewer(request, repository_name, file_blob)
140
-
        else:
+
139
            for blob in directory.blobs:
+
140
                if blob.name == path_parts[i]:
+
141
                    file_blob = blob
+
142
                    return file_view(request, repository_name, path, file_blob)
+
143
        else:
141
144
            directory = subdirectories[path_parts[i]]
142
-
    # Current state:
+
145
                if subdirectory.name == path_parts[i]:
+
146
                    directory = subdirectory
+
147
                    break
+
148
    # Current state:
143
149
    # The entire path was traversed, and we kept reaching subdirectories, which
144
150
    # means that the current directory was requested, and not a file. Thus,
145
151
    # display the directory.
146
152
    return directory_view(request, repository_name, directory)
147
-
+
153
148
154
def directory_view(request, repository_name, path, directory):
149
155
    """ Collects the given directories's files and subdirectories, and renders a
150
156
    template to display this data.
151
157
    """
152
158
    # Collecting all files:
153
159
    files = directory.blobs
154
160
    file_names = []
155
161
    for ffile in files:
156
162
        file_names.append(ffile.name)
157
163
    # Collecting all subdirectories:
158
164
    subdirectories = directory.trees
159
165
    subdirectory_names = []
160
166
    for subdirectory in subdirectories:
161
167
        subdirectory_names.append(subdirectory.name)
162
168
    # Collecting rendering information:
163
169
    template = "gitar/directory.html"
164
170
    context = standard_context()
165
171
    context["files"] = file_names
166
172
    context["subdirectories"] = subdirectory_names
167
173
    return render(request, template, context)
168
174
169
175
170
176
def file_viewer(request, repository_name, path, ffile):
171
-
    """ Collects the file contents of the given file path, and returns it to the
+
177
    """ Collects the file contents of the given file path, and returns it to the
172
178
    template, with the file contents already formatted in HTML using Pygments.
173
179
    """
174
180
+
181
    file_content = RepoInfo.read_file(ffile)
+
182