Merge pull request #5 from kristoferssolo/fossdb

Fossdb
This commit is contained in:
Kristofers Solo 2023-04-09 20:08:34 +03:00 committed by GitHub
commit 2d68f8da80
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 436 additions and 34 deletions

View File

@ -7,7 +7,7 @@ jobs:
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
strategy: strategy:
matrix: matrix:
os: [ubuntu-latest, windows-latest] os: [ubuntu-latest]
python-version: ["3.10"] python-version: ["3.10"]
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
@ -19,5 +19,5 @@ jobs:
run: | run: |
python -m pip install --upgrade pip python -m pip install --upgrade pip
pip install tox tox-gh-actions pip install tox tox-gh-actions
- name: Lint with tox - name: Test with tox
run: tox run: tox

View File

@ -0,0 +1,35 @@
from django.contrib import admin
from .host.models import HostingPlatform, ProjectHostingPlatform
from .language.models import ProgrammingLanguage, ProjectProgrammingLanguage
from .license.models import License
from .models import Project
from .tag.models import Tag
class ProjectProgrammingLanguageInline(admin.TabularInline):
model = ProjectProgrammingLanguage
extra = 1
class ProjectHostingPlatformInline(admin.TabularInline):
model = ProjectHostingPlatform
extra = 1
class ProjectAdmin(admin.ModelAdmin):
inlines = [ProjectProgrammingLanguageInline, ProjectHostingPlatformInline]
list_display = ("author", "name", "_languages", "_hosting_platform")
def _languages(self, object):
return " | ".join([i.language.language for i in object.projectprogramminglanguage_set.all()])
def _hosting_platform(self, object):
return " | ".join([i.hosting_platform.hosting_platform for i in object.projecthostingplatform_set.all()])
admin.site.register(HostingPlatform)
admin.site.register(Project, ProjectAdmin)
admin.site.register(License)
admin.site.register(ProgrammingLanguage)
admin.site.register(Tag)

View File

@ -4,6 +4,19 @@ from .models import Project
class ProjectForm(forms.ModelForm): class ProjectForm(forms.ModelForm):
class Meta: class Meta:
model = Project model = Project
fields = ["title", "description"] fields = ["name", "description", "licenses"]
widgets = {
"name": forms.TextInput(attrs={
"class": "form-control",
"placeholder": "Project name",
}),
"description": forms.Textarea(attrs={
"class": "form-control",
"placeholder": "Description",
}),
"licenses": forms.CheckboxSelectMultiple(),
}

View File

@ -0,0 +1,22 @@
from django import forms
from fossdb.models import Project
from .models import HostingPlatform, ProjectHostingPlatform
class HostingPlatformForm(forms.ModelForm):
url = forms.URLField()
class Meta:
model = HostingPlatform
fields = ["hosting_platform", "url"]
ProjectHostingPlatformFormSet = forms.inlineformset_factory(
Project,
ProjectHostingPlatform,
form=HostingPlatformForm,
extra=1,
can_delete=False
)

View File

@ -0,0 +1,17 @@
from django.db import models
class HostingPlatform(models.Model):
hosting_platform = models.CharField(max_length=100)
def __str__(self):
return self.hosting_platform
class ProjectHostingPlatform(models.Model):
project = models.ForeignKey("Project", on_delete=models.CASCADE)
hosting_platform = models.ForeignKey(HostingPlatform, on_delete=models.CASCADE)
url = models.URLField()
def __str__(self):
return f"{self.project} | {self.hosting_platform}"

View File

@ -0,0 +1,22 @@
from django import forms
from fossdb.models import Project
from .models import ProgrammingLanguage, ProjectProgrammingLanguage
class ProgrammingLanguageForm(forms.ModelForm):
percentage = forms.IntegerField(min_value=0, max_value=100)
class Meta:
model = ProgrammingLanguage
fields = ["language", "percentage"]
ProjectProgrammingLanguageFormSet = forms.inlineformset_factory(
Project,
ProjectProgrammingLanguage,
form=ProgrammingLanguageForm,
extra=1,
can_delete=True,
)

View File

@ -0,0 +1,17 @@
from django.db import models
class ProgrammingLanguage(models.Model):
language = models.CharField(max_length=100)
def __str__(self):
return self.language
class ProjectProgrammingLanguage(models.Model):
project = models.ForeignKey("Project", on_delete=models.CASCADE)
language = models.ForeignKey(ProgrammingLanguage, on_delete=models.CASCADE)
percentage = models.PositiveIntegerField()
def __str__(self):
return f"{self.project} | {self.language} | {self.percentage}%"

View File

@ -0,0 +1,9 @@
from django import forms
from .models import License
class LicenseForm(forms.ModelForm):
class Meta:
model = License
fields = ["short_name", "full_name", "url", "description"]

View File

@ -0,0 +1,11 @@
from django.db import models
class License(models.Model):
short_name = models.CharField(max_length=50)
full_name = models.CharField(max_length=100, blank=True, default="")
url = models.URLField(blank=True, default="")
description = models.TextField(blank=True, default="")
def __str__(self):
return self.short_name

View File

@ -1,8 +1,9 @@
# Generated by Django 4.1 on 2023-03-27 17:46 # Generated by Django 4.1.7 on 2023-04-09 10:17
from django.conf import settings from django.conf import settings
from django.db import migrations, models from django.db import migrations, models
import django.db.models.deletion import django.db.models.deletion
import uuid
class Migration(migrations.Migration): class Migration(migrations.Migration):
@ -15,13 +16,84 @@ class Migration(migrations.Migration):
operations = [ operations = [
migrations.CreateModel( migrations.CreateModel(
name='Project', name='HostingPlatform',
fields=[ fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('title', models.CharField(max_length=255)), ('hosting_platform', models.CharField(max_length=100)),
('description', models.TextField()), ],
('create_date', models.DateTimeField(auto_now_add=True)), ),
migrations.CreateModel(
name='License',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('short_name', models.CharField(max_length=50)),
('full_name', models.CharField(blank=True, default='', max_length=100)),
('url', models.URLField(blank=True, default='')),
('description', models.TextField(blank=True, default='')),
],
),
migrations.CreateModel(
name='ProgrammingLanguage',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('language', models.CharField(max_length=100)),
],
),
migrations.CreateModel(
name='Project',
fields=[
('uuid', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
('name', models.CharField(max_length=255)),
('description', models.TextField(blank=True, default='')),
('date_created', models.DateTimeField(auto_now_add=True)),
('author', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), ('author', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
], ],
), ),
migrations.CreateModel(
name='Tag',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=100)),
('description', models.TextField(blank=True, default='')),
('icon', models.ImageField(blank=True, null=True, upload_to='types/icons/')),
],
),
migrations.CreateModel(
name='ProjectProgrammingLanguage',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('percentage', models.PositiveIntegerField()),
('language', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='fossdb.programminglanguage')),
('project', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='fossdb.project')),
],
),
migrations.CreateModel(
name='ProjectHostingPlatform',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('url', models.URLField()),
('hosting_platform', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='fossdb.hostingplatform')),
('project', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='fossdb.project')),
],
),
migrations.AddField(
model_name='project',
name='hosting_platform',
field=models.ManyToManyField(related_name='projects', through='fossdb.ProjectHostingPlatform', to='fossdb.hostingplatform'),
),
migrations.AddField(
model_name='project',
name='licenses',
field=models.ManyToManyField(to='fossdb.license'),
),
migrations.AddField(
model_name='project',
name='programming_languages',
field=models.ManyToManyField(related_name='projects', through='fossdb.ProjectProgrammingLanguage', to='fossdb.programminglanguage'),
),
migrations.AddField(
model_name='project',
name='tag',
field=models.ManyToManyField(to='fossdb.tag'),
),
] ]

View File

@ -0,0 +1,27 @@
# Generated by Django 4.1.7 on 2023-04-09 10:35
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('fossdb', '0001_initial'),
]
operations = [
migrations.CreateModel(
name='OperatingSystem',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=100)),
('description', models.TextField(blank=True, default='')),
('version', models.CharField(blank=True, max_length=50)),
],
),
migrations.AddField(
model_name='project',
name='os',
field=models.ManyToManyField(to='fossdb.operatingsystem'),
),
]

View File

@ -0,0 +1,29 @@
# Generated by Django 4.1.7 on 2023-04-09 10:51
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('fossdb', '0002_operatingsystem_project_os'),
]
operations = [
migrations.CreateModel(
name='Star',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('project', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='stars', to='fossdb.project')),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
),
migrations.AddField(
model_name='project',
name='star',
field=models.ManyToManyField(related_name='projects_star', to='fossdb.star'),
),
]

View File

@ -1,15 +1,42 @@
from django.contrib.auth.models import User import uuid
from django.conf import settings
from django.db import models from django.db import models
from .host.models import HostingPlatform
from .language.models import ProgrammingLanguage
from .license.models import License
from .operating_system.models import OperatingSystem
from .star.models import Star
from .tag.models import Tag
User = settings.AUTH_USER_MODEL
class Project(models.Model): class Project(models.Model):
author = models.ForeignKey(User, on_delete=models.CASCADE) uuid = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
title = models.CharField(max_length=255) author = models.ForeignKey(User, on_delete=models.CASCADE, db_index=True)
description = models.TextField() name = models.CharField(max_length=255)
create_date = models.DateTimeField(auto_now_add=True) description = models.TextField(blank=True, default="")
licenses = models.ManyToManyField(License)
programming_languages = models.ManyToManyField(ProgrammingLanguage, through="ProjectProgrammingLanguage", related_name="projects")
hosting_platform = models.ManyToManyField(HostingPlatform, through="ProjectHostingPlatform", related_name="projects")
tag = models.ManyToManyField(Tag)
os = models.ManyToManyField(OperatingSystem)
star = models.ManyToManyField(Star, related_name="projects_star")
date_created = models.DateTimeField(auto_now_add=True)
def __str__(self): @property
return self.repo_name def star_amount(self):
return self.star.count()
def get_absolute_url(self): def get_absolute_url(self):
return f"/projects/{self.id}" return f"/{self.author.name}/{self.name}"
def __str__(self):
return f"{self.author} | {self.name}"
def save(self, *args, **kwargs):
if not self.uuid:
self.uuid = uuid.uuid3(uuid.uuid4(), f"{self.author.username}-{self.name}")
super().save(*args, **kwargs)

View File

@ -0,0 +1,7 @@
from django.db import models
class OperatingSystem(models.Model):
name = models.CharField(max_length=100)
description = models.TextField(blank=True, default="")
version = models.CharField(max_length=50, blank=True)

View File

@ -0,0 +1,9 @@
from django.conf import settings
from django.db import models
User = settings.AUTH_USER_MODEL
class Star(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
project = models.ForeignKey("Project", on_delete=models.CASCADE, related_name="stars")

View File

@ -0,0 +1,11 @@
from django.db import models
class Tag(models.Model):
name = models.CharField(max_length=100)
description = models.TextField(blank=True, default="")
icon = models.ImageField(upload_to="types/icons/", null=True, blank=True)
def __str__(self):
return self.name

View File

@ -2,24 +2,49 @@ from django.contrib.auth.decorators import login_required, permission_required
from django.shortcuts import redirect, render from django.shortcuts import redirect, render
from .forms import ProjectForm from .forms import ProjectForm
from .host.forms import ProjectHostingPlatformFormSet
from .language.forms import ProjectProgrammingLanguageFormSet
from .models import Project from .models import Project
def index(request): def index(request):
projects = Project.objects.all() context = {
return render(request, "fossdb/index.html", {"title": "FOSSDB", "projects": projects}) "title": "FOSSDB",
"projects": Project.objects.all(),
}
return render(request, "fossdb/index.html", context)
@login_required(login_url="login/") @login_required(login_url="login/")
@permission_required("fossdb.add_post)", login_url="login/", raise_exception=True) @permission_required("fossdb.add_project", login_url="login/", raise_exception=True)
def add_project(request): def add_project(request):
if request.method == "POST": if request.method == "POST":
form = ProjectForm(request.POST) project_form = ProjectForm(request.POST)
if form.is_valid(): language_formset = ProjectProgrammingLanguageFormSet(request.POST, instance=Project())
post = form.save(commit=False) host_formset = ProjectHostingPlatformFormSet(request.POST, instance=Project())
post.author = request.user
post.save() if project_form.is_valid() and language_formset.is_valid() and host_formset.is_valid():
project = project_form.save(commit=False)
project.author = request.user
project.save()
language_formset.instance = project
language_formset.save()
host_formset.instance = project
host_formset.save()
project_form.save_m2m()
return redirect("/") return redirect("/")
else: else:
form = ProjectForm() project_form = ProjectForm()
return render(request, "fossdb/add_project.html", {"title": "Add project", "form": form}) language_formset = ProjectProgrammingLanguageFormSet()
host_formset = ProjectHostingPlatformFormSet()
context = {
"title": "Add project",
"project_form": project_form,
"language_formset": language_formset,
"host_formset": host_formset,
}
return render(request, "fossdb/add_project.html", context)

View File

@ -36,8 +36,8 @@ ALLOWED_HOSTS = config["ALLOWED_HOSTS"]
# Application definition # Application definition
INSTALLED_APPS = [ INSTALLED_APPS = [
"fossdb",
"account", "account",
"fossdb",
"django.contrib.admin", "django.contrib.admin",
"django.contrib.auth", "django.contrib.auth",
"django.contrib.contenttypes", "django.contrib.contenttypes",
@ -82,7 +82,7 @@ WSGI_APPLICATION = "FOSSDB_web.wsgi.application"
DATABASES = { DATABASES = {
"default": { "default": {
"ENGINE": "django.db.backends.mysql", "ENGINE": f"django.db.backends.{config['DATABASE']['ENGINE']}",
"NAME": config["DATABASE"]["NAME"], "NAME": config["DATABASE"]["NAME"],
"USER": config["DATABASE"]["USER"], "USER": config["DATABASE"]["USER"],
"PASSWORD": config["DATABASE"]["PASSWORD"], "PASSWORD": config["DATABASE"]["PASSWORD"],
@ -128,6 +128,7 @@ USE_TZ = True
STATIC_URL = "/static/" STATIC_URL = "/static/"
STATIC_ROOT = BASE_PATH.joinpath("static") STATIC_ROOT = BASE_PATH.joinpath("static")
MEDIA_URL = "/media/"
MEDIA_ROOT = BASE_PATH.joinpath("media") MEDIA_ROOT = BASE_PATH.joinpath("media")
# Default primary key field type # Default primary key field type
@ -136,3 +137,14 @@ MEDIA_ROOT = BASE_PATH.joinpath("media")
DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField" DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
LOGIN_REDIRECT_URL = "/" LOGIN_REDIRECT_URL = "/"
LOGOUT_REDIRECT_URL = "/" LOGOUT_REDIRECT_URL = "/"
# HTTPS settings
# SESSION_COOKIE_SECURE = True
# CSRF_COOKIE_SECURE = True
# SECURE_SSL_REDIRECT = True
# HSTS settings
# SECURE_HSTS_SECONDS = 31536000 # 1 year
# SECURE_HSTS_SECONDS = 1 # 1 year
# SECURE_HSTS_PRELOAD = True
# SECURE_HSTS_INCLUDE_SUBDOMAINS = True

View File

@ -1,10 +1,8 @@
{ {
"SECRET_KEY": "", "SECRET_KEY": "",
"ALLOWED_HOSTS": [ "ALLOWED_HOSTS": [""],
"",
"localhost"
],
"DATABASE": { "DATABASE": {
"ENGINE": "",
"NAME": "", "NAME": "",
"USER": "", "USER": "",
"PASSWORD": "", "PASSWORD": "",

View File

@ -1,3 +1,4 @@
Django==4.1.7 Django==4.1.7
Pillow==9.4.0
fontawesomefree==6.2.1 fontawesomefree==6.2.1
mysqlclient==2.1.1 mysqlclient==2.1.1

View File

@ -12,6 +12,7 @@ classifiers =
packages = FOSSDB_web packages = FOSSDB_web
install_requires = install_requires =
Django>=4.1 Django>=4.1
Pillow>=9.4.0
fontawesomefree>=6.2.1 fontawesomefree>=6.2.1
mysqlclient>=2.1.1 mysqlclient>=2.1.1

View File

@ -4,7 +4,19 @@
{% block meta %}{% endblock %} {% block meta %}{% endblock %}
{% block content %} {% block content %}
<form method="post"> <form method="post">
{% csrf_token %}{{ form }} {% csrf_token %}
{{ project_form.as_p }}
<table>
{{ language_formset.management_form }}
{% for form in language_formset %}
<tr>
<td>{{ form.language.label_tag }}</td>
<td>{{ form.language }}</td>
<td>{{ form.percentage }}</td>
</tr>
{% endfor %}
</table>
{{ host_formset.as_p }}
<button type="submit">Post</button> <button type="submit">Post</button>
</form> </form>
{% endblock %} {% endblock %}

View File

@ -3,10 +3,35 @@
{% block title %}{{ title }}{% endblock %} {% block title %}{{ title }}{% endblock %}
{% block meta %}{% endblock %} {% block meta %}{% endblock %}
{% block content %} {% block content %}
<button >
<a href="{% url 'add-project' %}">Add Project</a>
</button>
<button >
<a href="/admin">Admin</a>
</button>
<br>
{% for project in projects %} {% for project in projects %}
<b>@{{ project.author.username }}</b> <b>@{{ project.author.username }}</b>
<h5>{{ project.title }}</h5> <h4>{{ project.name }}</h4>
<p>{{ project.description }}</p> <p>{{ project.description }}</p>
<ul>
{% for license in project.licenses.all %}
<li>
<a href="{{ license.url }}">{{ license.short_name }}</a>
</li>
{% empty %}
<p>No license</p>
{% endfor %}
</ul>
<ul>
{% for language in project.projectprogramminglanguage_set.all %}
<li>{{ language.language }} | {{ language.percentage }}&#37;</li>
{% empty %}
<p>No language</p>
{% endfor %}
</ul>
<br>
<p>{{ project.date_created }}</p>
{% empty %} {% empty %}
<p>No projects yet (</p> <p>No projects yet (</p>
{% endfor %} {% endfor %}