Browse Source

Started working on core logic

main
root 5 months ago
parent
commit
4e98346b85
  1. 2
      .gitignore
  2. 1
      MANIFEST.in
  3. 6
      README.md
  4. 19
      cutegit/app.py
  5. 3
      cutegit/core.py
  6. 0
      cutegit/git/__init__.py
  7. 72
      cutegit/git/backend.py
  8. 18
      cutegit/templates/homepage.html
  9. 1
      cutegit/templates/repository/index.html
  10. 51
      cutegit/views.py
  11. 1
      setup.py

2
.gitignore

@ -1,3 +1,5 @@
.idea/
venv/
__pycache__/
*.egg-info/
dist/

1
MANIFEST.in

@ -1,2 +1,3 @@
include cutegit/templates/*
include cutegit/templates/**/*
include cutegit/static/*

6
README.md

@ -1,8 +1,8 @@
# cutegit
# CuteGit
![trans rights!](https://awoo.systems/trans_rights_badge.svg)
Web UI for barebones git repositories, alternative to webgit.
Web UI for bare-bones git repositories, alternative to webgit.
## Quickstart
@ -35,3 +35,5 @@ $ FLASK_ENV=cutegit:app flask run
- [Git] README.md auto-render
- [Git] Render cache (redis, probably)
- [Core] Config to hide or show authors, defaults to hiding
- [Core] Additional metadata configuration (e.g. links and such)
- [Core] Markdown support in description

19
cutegit/app.py

@ -1,8 +1,21 @@
from flask import Flask, render_template
from flask import render_template, redirect, url_for, flash
app = Flask(__name__)
# noinspection PyUnresolvedReferences
from . import views
from .core import app
from .git.backend import get_git, group_by_category
@app.route('/')
def homepage():
return render_template('homepage.html')
repos = group_by_category(get_git().repositories())
return render_template('homepage.html', repositories=repos)
@app.route('/~<string:repo>')
def repository_index(repo: str):
repository = get_git().get_repository(repo)
if repository is None:
flash(f'No repository with name {repo}')
return redirect(url_for('homepage'))
return render_template('repository/index.html', name=repo, repo=repository)

3
cutegit/core.py

@ -0,0 +1,3 @@
from flask import Flask
app = Flask(__name__)

0
cutegit/git/__init__.py

72
cutegit/git/backend.py

@ -0,0 +1,72 @@
from itertools import groupby
from os import listdir
from pathlib import Path
from typing import Optional, Iterable, List
from flask import g, current_app
from pygit2 import Repository as _Repository, GitError
class Repository(_Repository):
file_cache: dict
def __init__(self, path=None, flags=0):
super().__init__(path, flags)
self.file_cache = {}
def get_name(self) -> str:
return Path(self.path).name
def get_from_file(self, file: str, default_value: Optional[str] = None, fresh=False) -> Optional[str]:
path = Path(self.path) / file
if file in self.file_cache and not fresh:
return self.file_cache[file]
if path.is_file():
with open(path, 'r') as f:
content = f.read().strip()
else:
content = default_value
self.file_cache[file] = content
return self.file_cache[file]
def get_category(self) -> str:
return self.get_from_file('category', '')
def get_description(self) -> str:
return self.get_from_file('description', '*No description*')
class GitBackend:
root: Path
def __init__(self, root: Path):
self.root = root
def repositories(self) -> Iterable[Repository]:
return filter(
lambda r: r.is_bare,
map(lambda r: Repository(self.root / r), listdir(self.root))
)
def get_repository(self, name: str) -> Optional[Repository]:
try:
return Repository(self.root / name)
except GitError:
return None
def get_git():
if 'git' not in g:
g.git = GitBackend(Path(
current_app.config['GIT_STORE'] if 'GIT_STORE' in current_app.config
else '/srv/git'))
return g.git
def group_by_category(repos: Iterable[Repository]) -> dict[str, List[Repository]]:
sorted_repos = sorted(repos, key=lambda r: r.get_category())
return dict({x[0]: list(x[1]) for x in groupby(sorted_repos, lambda r: r.get_category())})

18
cutegit/templates/homepage.html

@ -1,3 +1,19 @@
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
<h1>Meow</h1>
<h1 class="brand">Cute<span>Git</span></h1>
{% for category in repositories %}
<section>
<h2>{{ category }}</h2>
{% for repo in repositories[category] %}
<article class="repository">
<h3><a href="{{ url_for('repository_index', repo=repo.get_name()) }}">{{ repo.get_name() }}</a></h3>
<p>{{ repo.get_description() }}</p>
<p title="The {{ repo[repo.head.target].commit_time|date_iso8601 }}">
Updated {{ repo[repo.head.target].commit_time|date_human }}
</p>
</article>
{% endfor %}
</section>
{% endfor %}

1
cutegit/templates/repository/index.html

@ -0,0 +1 @@
<h1>{{ name }}</h1>

51
cutegit/views.py

@ -0,0 +1,51 @@
from time import time
from datetime import datetime, timedelta
from .core import app
@app.template_filter('date_human')
def date_human(v):
return readable_delta(v)
def readable_delta(from_seconds, until_seconds=None):
"""Returns a nice readable delta.
readable_delta(1, 2) # 1 second ago
readable_delta(1000, 2000) # 16 minutes ago
readable_delta(1000, 9000) # 2 hours, 133 minutes ago
readable_delta(1000, 987650) # 11 days ago
readable_delta(1000) # 15049 days ago (relative to now)
See https://stackoverflow.com/a/5333305
"""
if not until_seconds:
until_seconds = time()
seconds = until_seconds - from_seconds
delta = timedelta(seconds=seconds)
# deltas store time as seconds and days, we have to get hours and minutes ourselves
delta_minutes = delta.seconds // 60
delta_hours = delta_minutes // 60
# show a fuzzy but useful approximation of the time delta
if delta.days:
return '%d day%s ago' % (delta.days, plural(delta.days))
elif delta_hours:
return '%d hour%s, %d minute%s ago' % (delta_hours, plural(delta_hours), delta_minutes, plural(delta_minutes))
elif delta_minutes:
return '%d minute%s ago' % (delta_minutes, plural(delta_minutes))
else:
return '%d second%s ago' % (delta.seconds, plural(delta.seconds))
def plural(it):
return '' if it == 1 else 's'
@app.template_filter('date_iso8601')
def date_iso8601(v):
return datetime.fromtimestamp(v).isoformat(' ')

1
setup.py

@ -19,6 +19,7 @@ setup(
packages=find_packages(),
install_requires=[
'Flask',
'pygit2',
],
classifiers=[
'Environment :: Console',

Loading…
Cancel
Save